Przejdź do głównej zawartości

Zajęcia wakacyjne

Kompleksowy przewodnik po module zajęć wakacyjnych — pojedynczych zajęć w kalendarzu (np. „Doubles (wakacyjne)"), na które uczestnik zapisuje się termin po terminie. Obejmuje ustawienia, warunki zapisu, akceptację regulaminu, przepływy płatności, wygaśnięcia oraz zapis wszystkiego w historii.

:::note Rozróżnienie To nie to samo co moduł Półkolonie (camps). Półkolonie to wielodniowe turnusy z własnym systemem sezonów, list rezerwowych i dokumentów. Zajęcia wakacyjne opisane tutaj to zwykłe gry (game) z rodzajem zajęć w kategorii „Wakacyjne", na które klient zapisuje się pojedynczo, z dedykowaną logiką zapisu, regulaminu i płatności. :::

Przepływ w skrócie (diagram)

👤 Instrukcja dla pracownika (Administracja / Recepcja / Instruktor)

Czym są zajęcia wakacyjne

Zajęcia wakacyjne to rodzaj zajęć (Rodzaj zajęć) oznaczony kategorią „Wakacyjne". Zajęcia utworzone z takiego rodzaju trafiają na osobną zakładkę „Zajęcia wakacyjne" w portalu klienta i podlegają odrębnym regułom widoczności, zapisu i płatności.

Ustawienia (Panel administratora → Ustawienia zajęć wakacyjnych)

Dla każdego miasta konfiguruje się osobno:

  • Włącz zajęcia wakacyjne — globalny przełącznik dostępności modułu w danym mieście.
  • Natychmiastowa płatność (online) — gdy włączona, klient zapisujący się sam jest od razu kierowany do płatności; Czas na płatność online (minuty) określa, ile ma czasu, zanim zapis wygaśnie.
  • Natychmiastowa płatność (pracownik) — gdy recepcja zapisuje uczestnika, do klienta wysyłany jest link do płatności (SMS); Czas na płatność pracownik (minuty) określa okno na opłacenie.
  • Dni z wyprzedzeniem (klient) / Dni z wyprzedzeniem (pracownik) — jak daleko w przód widać dostępne terminy (osobno dla klienta i pracownika).

Zapis uczestnika

Zapis możliwy jest przez klienta (portal klienta → zakładka „Zajęcia wakacyjne") oraz przez recepcję (w imieniu uczestnika). System dopuszcza zapis tylko, gdy spełnione są wszystkie warunki:

  1. Ukończona ocena umiejętności (zajęcia próbne) — nowy uczestnik musi najpierw przejść zajęcia próbne / ocenę umiejętności, zanim zobaczy ofertę wakacyjną.
  2. Brak zaległej / oczekującej płatności wakacyjnej — uczestnik z nieopłaconą, aktywną płatnością wakacyjną nie może zapisać się na kolejny termin, dopóki jej nie ureguluje lub nie wygaśnie.
  3. Dopasowanie wieku — termin jest oferowany tylko, gdy jego zakres wiekowy pokrywa się z przypisanym uczestnikowi rodzajem zajęć („Rodzaj zajęć"). Jeśli uczestnik nie ma jeszcze przypisanej grupy, widzi komunikat o oczekiwaniu na przypisanie przez recepcję.
  4. Wolne miejsca i okno widoczności (dni z wyprzedzeniem).

Akceptacja regulaminu (i RODO)

Zapis na zajęcia wakacyjne wymaga akceptacji regulaminu wakacyjnej szkółki oraz RODO. Akceptacja jest wymuszana na każdej ścieżce płatności:

  • podczas zapisu przez klienta (dialog zgody),
  • na stronie linku do płatności (SMS od recepcji),
  • w widoku Płatności w aplikacji, jeśli klient zamiast klikać w link zaloguje się i opłaci zajęcia z listy płatności.

Bez zaznaczenia zgody nie da się przejść do płatności. Fakt akceptacji jest zapisywany i widoczny w historii (patrz niżej).

Ostrzeżenie o braku możliwości odwołania

Jeśli termin zajęć przypada wcześniej niż wymagany czas na anulowanie (próg anulowania rodzaju zajęć), przy wyborze terminu pojawia się ostrzeżenie:

„Uwaga! Termin zajęć przypada wcześniej niż wymagany czas na jej anulowanie. Po dokonaniu zapisu nie będzie możliwości odwołania zajęć."

Dotyczy to zarówno zajęć wakacyjnych, jak i wakacyjnych zajęć próbnych.

Płatność

  • Klient (natychmiastowa płatność online) — po zapisie klient trafia do widoku Płatności (/dashboard/payments), gdzie po akceptacji regulaminu opłaca zajęcia. Zajęcia wakacyjne nie są automatycznie przekierowywane wprost na bramkę Przelewy24 — klient reguluje je z poziomu listy płatności (bramką lub portfelem).
  • Recepcja — po zapisie do klienta wychodzi link do płatności (SMS); na stronie linku klient akceptuje regulamin i płaci (Przelewy24).
  • Po zaksięgowaniu płatności klient dostaje powiadomienie „Płatność potwierdzona" (vacation_payment_success).

Wygaśnięcie płatności

Jeśli uczestnik nie opłaci w wyznaczonym czasie, płatność wygasa, a miejsce jest zwalniane automatycznie. Wygaśnięcie jest odnotowane w historii zajęć jako „Wygaśnięcie czasu na opłacenie".

Historia

Wszystkie zdarzenia związane z zajęciami wakacyjnymi trafiają do dwóch historii:

  • Historia Zajęć (podgląd/edycja pojedynczej gry) — chronologiczny log zdarzeń danej gry: dołączenie, akceptacja regulaminu, płatność, płatność z portfela, zwrot na portfel, usunięcie bez zwrotu, wygaśnięcie, odwołanie.
  • Historia aktywności (/dashboard/history) — ogólny rejestr, w którym zajęcia wakacyjne mają własne źródło „Zajęcia wakacyjne" oraz m.in. wpis „Akceptacja regulaminu".

🛠️ Dokumentacja techniczna

Model danych

  • Rodzaj zajęć wakacyjnych: activity_types.category = 'Wakacyjne' (typ at.type = 'other'). To jedyny wyróżnik odróżniający zajęcia wakacyjne od zwykłych.
  • Gra: zwykły rekord game z activity_type_id wskazującym na rodzaj „Wakacyjne".
  • Uczestnik wakacyjny: wpis w game.attendees z type = 'vacation' i paymentId.
  • Płatność: payment.payment_type = 'school', related_ids = [gameId]. Zajęcia wakacyjne rozpoznaje się jako payment_type = 'school' oraz activity_type.category = 'Wakacyjne'.
  • Log zdarzeń gry (game.event_log, JSON): m.in. joined, consent_accepted, payment_paid, paid_with_wallet, refund_to_wallet, removed_without_refund, attendee_expired, cancelled.

Ustawienia

VacationActivitySettings w lib/actions/app-settings.ts (klucze app_settings per miasto): vacation_activity_enabled, vacation_activity_immediate_payment_online, vacation_activity_immediate_payment_employee, vacation_activity_payment_time_online, vacation_activity_payment_time_employee, vacation_activity_payment_time (legacy fallback, nie edytowany już w UI), vacation_activity_days_advance_client, vacation_activity_days_advance_employee.

Formularz: components/forms/vacation-activity-settings/vacation-activity-settings-form.tsx (w UI zostały tylko dwa pola czasu płatności: online i pracownik).

Zapis i widoczność

  • enrollInVacationActivity(gameId, playerId, city, consentAccepted) — serwerowa akcja zapisu. Wymaga consentAccepted (inaczej errors.consentRequired), blokuje przy zaległościach, zapisuje uczestnika typu vacation oraz zdarzenie consent_accepted do event_log.
  • hasPendingVacationPaymentByPlayer(playerId) — blokada zapisu przy aktywnej, nieopłaconej płatności wakacyjnej (świadoma wygaśnięć).
  • getGamesByDateRangeForPlayer(...) — źródło dostępnych terminów; zwraca activity_type z category, cancellation_hours_before i innymi polami potrzebnymi UI.
  • useVacationEnrollment(...) (components/forms/vacation-enrollment/use-vacation-enrollment.ts) — hook ładujący ustawienia, dostępne terminy, status zaległości i informację o konieczności przypisania do grupy. Dopasowanie wieku: vacationTermMatchesAssignedTypes(...).
  • Bramka zajęć próbnych (ocena umiejętności) przed ofertą wakacyjną: playerMustCompleteSkillAssessment(...).
  • UI zapisu: components/forms/vacation-enrollment/vacation-enrollment-dialog.tsx (zajęcia wakacyjne) oraz components/forms/trial-enrollment/trial-enrollment-dialog.tsx z propem vacationOnly (wakacyjne zajęcia próbne).

Płatności

  • Brak auto-redirectu na bramkę dla wakacji: po zapisie klient trafia do /dashboard/payments (a nie wprost na Przelewy24). Wakacyjne zajęcia próbne bramkowane propem vacationOnly; zwykłe próbne bez zmian.
  • Link do płatności: app/pay/[token]/page.tsx + PublicPayButton.tsx; inicjalizacja app/api/public/payment/initialize/route.ts.
  • Wykrywanie płatności wakacyjnej w widoku Płatności: pole activity_type_category wystawione z zapytań płatności (lib/actions/payment.ts), warunek payment_type === 'school' && activity_type_category === 'Wakacyjne'.

Akceptacja regulaminu jest zapisywana jako zdarzenie consent_accepted w event_log gry na każdej ścieżce płatności:

  • Widok Płatności (w aplikacji) — bramka VacationStatuteAcceptanceDialog (components/modal/vacation-statute-acceptance-dialog.tsx) wpięta w payments-list-view-user.tsx i PaymentsTableAccordion.tsx (analogicznie do bramki rezerwacji kortów); po akceptacji saveVacationStatuteAcceptance(paymentIds).
  • Link do płatnościapp/api/public/payment/initialize/route.ts wywołuje recordVacationStatuteConsent(paymentId, acceptedBy, tenantId) dla płatności typu group (updateBookingStatuteAcceptance jest no-opem dla gier — dlatego trzeba zapisać zdarzenie na grze).
  • Zapis przy zapisieenrollInVacationActivity zapisuje consent_accepted przy dołączeniu.

Rdzeń zapisu: recordVacationStatuteConsent(...) w lib/actions/payment.ts — bez zależności od sesji, best-effort, z dedupem po paymentId (nie dubluje zgody). Dialog linkuje do Regulaminu wakacyjnej szkółki oraz RODO.

Historia — renderowanie

  • Historia aktywności: builder buildGameConsentAcceptedSQL w lib/actions/activity-log.ts (typ akcji consent_accepted, źródło vacation); render w app/(dashboard)/dashboard/history/components/ActivityLogItem.tsx (badge „Akceptacja regulaminu"). Zajęcia wakacyjne mają źródło source_type = 'vacation'.
  • Historia Zajęć: app/(dashboard)/dashboard/schedule/components/GameEventsView.tsx renderuje wszystkie zdarzenia z event_log; etykiety/opisy z gameEvents.eventTypes.* i eventDescriptions.* (m.in. consent_accepted, payment_paid, paid_with_wallet, removed_without_refund, game_cancelled).

Wygaśnięcia i zwolnienie miejsca

  • Cron expirePendingPayments(env) (lib/actions/expire-payments.ts) przełącza wygasłe płatności pending → expired; logExpiriesToGameLog(...) dopisuje zdarzenie attendee_expired do event_log gry (dla płatności school na grach „Wakacyjne" oraz trial na dowolnej grze). Miejsce zwalniane jest przez leniwe filtry uczestników.

Powiadomienia

  • vacation_payment_success (lib/notification-config.ts) — SMS/e-mail po zaksięgowaniu płatności; szablon e-mail: mails/vacation-payment-success.html.

Kluczowe pliki

ObszarPlik
Ustawienialib/actions/app-settings.ts, components/forms/vacation-activity-settings/vacation-activity-settings-form.tsx
Zapislib/actions/game.ts (enrollInVacationActivity), components/forms/vacation-enrollment/*
Dostępne terminylib/actions/game.ts (getGamesByDateRangeForPlayer), use-vacation-enrollment.ts
Regulamincomponents/modal/vacation-statute-acceptance-dialog.tsx, lib/actions/payment.ts (recordVacationStatuteConsent, saveVacationStatuteAcceptance)
Płatność (link)app/pay/[token]/*, app/api/public/payment/initialize/route.ts
Płatność (widok)components/tables/payments-table/payments-list-view-user.tsx, PaymentsTableAccordion.tsx
Historialib/actions/activity-log.ts, ActivityLogItem.tsx, GameEventsView.tsx
Wygaśnięcialib/actions/expire-payments.ts
Powiadomienialib/notification-config.ts, mails/vacation-payment-success.html