Przejdź do głównej zawartości

Wybór obiektu w zakładce Rezerwacje

Instrukcja

Dotyczy zakładki Rezerwacje w aplikacji klienta, w miastach z więcej niż jednym obiektem (np. Opole: ul. Oleska i ul. Spokojna).

Co widzi klient

  1. Miasto (klub) klient wybiera na stronie logowania — kafelek „AcePark Opole / Oleska · Spokojna / Zmień” pod logo jest żółty i od razu widoczny; kliknięcie otwiera listę klubów. Wewnątrz aplikacji klient nie zmienia miasta.
  2. W górnej belce, po lewej od avatara, klient ma jeden żółty przycisk lokalizacji — na komputerze „Opole · ul. Spokojna · Zmień”, na telefonie pigułkę z nazwą ulicy („Spokojna”). Pokazuje się tylko w mieście z więcej niż jednym obiektem i tylko na stronach, na których lokalizacja ma znaczenie (Rezerwacje, Uczestnicy). Kliknięcie otwiera listę Wybierz obiekt z obiektami tego miasta — na komputerze jako panel pod przyciskiem, na telefonie jako panel wysuwany od dołu. Każdy obiekt to karta z nazwą, miastem i plakietką:
    • Rezerwacje online — w tym obiekcie można rezerwować kort z aplikacji,
    • Brak rezerwacji online — obiekt bez rezerwacji online; po wybraniu widać tylko listę własnych rezerwacji i komunikat „Rezerwacje niedostępne”.
  3. Wybrany obiekt jest zapamiętywany (ciasteczko na 30 dni). Przy następnym wejściu w Rezerwacje bez parametrów w adresie strona otwiera się na ostatnio wybranym obiekcie zamiast na domyślnej ul. Spokojnej.
  4. Na telefonie panel otwiera się sam przy wejściu w Rezerwacje tylko wtedy, gdy klient nie ma jeszcze zapamiętanego obiektu. Po pierwszym wyborze nic nie wyskakuje. Na komputerze nic nie otwiera się automatycznie.

Co widzi pracownik

Pracownik (ADMIN / BACKOFFICE) ma w belce jeden żółty przycisk w tym samym designie z listą: „Wszystkie miasta” (tam, gdzie widok nie jest ograniczony do jednego miasta), potem każde miasto jako całość („Opole · Wszystkie lokalizacje”) i pod nim jego obiekty („ul. Oleska”, „ul. Spokojna”). Wybór ulicy z innego miasta to jedno dotknięcie. Na stronach bez wymiaru ulicy (np. Płatności) lista zawiera tylko miasta. Reguły, na których stronach jest wybór miasta lub ulicy, są bez zmian względem dawnych selectów.

Po co

Domyślnym obiektem klienta była ul. Spokojna (NEXT_PUBLIC_DEFAULT_STREET). Gdy rezerwacje online są tam wyłączone, klient widział tylko komunikat „Rezerwacje niedostępne” i pusty widok, a ikona zmiany lokalizacji w belce nie była dla niego oczywista. Wymuszony wybór przy wejściu pokazuje od razu, że są dwa obiekty i w którym z nich można rezerwować.

Dokumentacja techniczna

Dane

getBookableLocationOptions(city) w lib/actions/booking-settings.ts zwraca oprócz streets (otwarte online) i enabled także allStreets — wszystkie ulice miasta z tabeli court. getAllLocationOptions() składa z tego listę HeaderLocationOption { city, street | null, bookable } dla wszystkich miast (getCities() + getBookableLocationOptions per miasto, równolegle) i trzyma ją w cache booking-settings:<tenant> z TTL 120 s — belka renderuje się na każdej stronie, więc bez cache byłoby to 2 zapytania na miasto przy każdym wejściu. Zmiana allow_online_booking unieważnia ten namespace od razu; zmiana reservation_enabled w app_settings propaguje się po wygaśnięciu TTL.

components/layout/global-city-select.tsx ładuje tę listę tylko dla klienta i przekazuje jako locations. Strona reservations/page.tsx nadal buduje własne locationOptions (obiekty bieżącego miasta) dla mobilnego panelu przy wejściu.

Zapamiętana lokalizacja

Ciasteczko dotyczy ulicy w mieście klienta: page.tsx i selektor biorą z niego tylko street, i tylko gdy city z ciasteczka zgadza się z pokazywanym miastem. Zapamiętane inne miasto jest ignorowane.

  • Ciasteczko client-location (lib/location-cookies.ts) o wartości miasto|ulica (ulica pusta dla miasta bez ulic), 30 dni, sameSite=lax. Zapis: server action rememberClientLocation(city, street) w lib/utils/location.ts, wołany z handleLocationChange w belce i z handleLocationSelect w ReservationsClient (mobilny panel). Odczyt: getRememberedClientLocation() — zwraca null bez ciasteczka lub poza zakresem żądania.
  • To celowo inne ciasteczko niż selected-city / selected-street: tamte karmią zapisy po stronie serwera i muszą wskazywać własny klub klienta nawet wtedy, gdy przegląda korty w innym mieście (isBorrowedCity). W selektorze własne miasto to teraz ownCity (z preferred_city), a fallbackCity do URL-a to zapamiętane miasto na /dashboard/reservations albo ownCity wszędzie indziej.
  • reservations/page.tsx: bez ?city= bierze zapamiętane miasto (jeśli ma korty), bez ?street= — zapamiętaną ulicę, o ile dotyczy wybranego miasta i nadal w nim istnieje. Ulica, której klub już nie ma (zmiana adresu, zamknięcie obiektu), jest pomijana: filtr po nieistniejącej ulicy pokazywał „brak rezerwacji online" bez niczego w URL-u, co by to tłumaczyło. Wtedy strona wraca do całego miasta, a na mobile pyta o obiekt jeszcze raz. Selektor w belce liczy ten sam fallback (rememberedCity, rememberedStreet przed DEFAULT_STREET), więc router.replace, który dopisuje parametry do URL-a, zgadza się z pierwszym renderem.
  • Pracownicy ciasteczka nie zapisują ani nie czytają.

Komponenty

  • components/location/option-card-list.tsx — generyczna lista kart (OptionCard { key, label, description?, badge?, icon?, indent? }, role="option", id="<prefix>-<key>"). density="compact" (popover na desktopie) daje niskie wiersze bez ramek, ulice wcięte pod miastem; domyślne comfortable (drawer na mobile) to duże karty.
  • components/location/location-option-list.tsxLocationOption { city, street | null, bookable } → karty z plakietką (useLocationOptionCards), klucz locationOptionKey(city, street).
  • components/location/location-picker-button.tsx — żółta pigułka w belce: desktop #<prefix>-select + Popover (align="end", 380 px), mobile #<prefix>-select-mobile + Drawer; tytuł i opcjonalny opis.
  • components/layout/global-city-select-client.tsx — wszystkie efekty korygujące URL (miasto z preferred_city, domyślna ulica, ciasteczko lokalizacji) bez zmian. Klient: jeden LocationPickerButton (header-location) z ulicami selectedCity (z locations, z plakietkami), handleStreetChange zapisuje też client-location. Pracownik: jeden przycisk header-location z kluczami ALL, <miasto> (całe miasto → street=ALL gdy miasto ma wymiar ulicy, inaczej bez street) i <miasto>|<ulica>; locations są ładowane także dla pracownika, więc ulice każdego miasta są znane bez fetchStreetsByCity.
  • app/(dashboard)/dashboard/reservations/components/LocationPicker.tsxLocationDrawer + LocationOptionList używane przez ReservationsClient wyłącznie na mobile (promptLocationOnEntry = hasLocationChoice && isMobile && !hasRememberedLocation).
  • app/auth/login/login-entry.tsx — kafelek wybranego klubu (#selected-club) w żółtej ramce z ulicami klubu i chipem „Zmień”.

Tłumaczenia

Namespace tennis (baseline ładowany na każdej stronie, bo belka jest w layoucie): chooseLocationTitle, chooseLocationDescription, locationBookable, locationNotBookable, changeLocation, streetPrefix, selectLocation.

Testy

  • __tests__/components/reservations-client-location-picker.test.tsx — otwarcie przy wejściu na mobile, brak panelu na desktopie / przy jednej ulicy / dla pracownika, nawigacja po wyborze (street, city, bez page), brak nawigacji przy ponownym wyborze tej samej ulicy.
  • __tests__/components/global-city-select-client-location-picker.test.tsx — wszystkie lokalizacje na Rezerwacjach, nawigacja z city + street, brak street dla miasta bez ulic, tylko własne miasto poza Rezerwacjami, brak przycisku na stronach bez wyboru, klasyczne selecty dla pracownika, zapis ciasteczka przy wyborze, fallback URL-a z zapamiętanej lokalizacji (miasto i ulica) oraz jego ignorowanie poza Rezerwacjami.

Powiązane: Ustawienia rezerwacji — reguły, kiedy lokalizacja jest otwarta online.