Poziom uczestnika zajęć próbnych
Trener w trakcie zajęć próbnych wskazuje uczestnikowi poziom — czyli grupę zajęciową, na którą uczestnik powinien trafić po próbnych. Poziomem jest wprost rodzaj zajęć grupowych, więc wybór trenera od razu nadaje się do filtrowania wolnych terminów w dalszych krokach zapisu.
👤 Instrukcja dla trenera
Ścieżka: Dashboard ➔ Kalendarz ➔ [kliknij swoje zajęcia] ➔ Sprawdź obecność
Po co to jest
Uczestnik zajęć próbnych dołącza do normalnej grupy. To Ty na miejscu widzisz, czy dziecko poradzi sobie w tej grupie, czy pasuje mu inny poziom. Twój wybór trafia prosto do karty uczestnika i decyduje o tym, jakie terminy zaproponujemy rodzicowi po zajęciach.
Jak przypisać poziom
- Otwórz swoje zajęcia w kalendarzu i kliknij Sprawdź obecność.
- Uczestnicy zajęć próbnych mają pod nazwiskiem dodatkowe pole Poziom. Zwykli uczestnicy zajęć go nie mają — u nich nic się nie zmienia.
- Pole startuje puste. Grupa, w której odbywają się próbne, nie jest podpowiadana: uczestnik trafił do niej dlatego, że było w niej wolne miejsce i pasowała godzina, a nie dlatego, że odpowiada jego poziomowi. Wybór ma być Twoją oceną, nie zatwierdzeniem przypadku.
- Możesz wskazać kilka poziomów, jeśli uczestnik pasuje do więcej niż jednej grupy.
- Po ponownym wejściu pole pokazuje poziomy aktualnie przypisane — widzisz stan wyjściowy, także ten nadany wcześniej przez biuro.
- Możesz zarówno dodać poziom, jak i odznaczyć ten, który już jest — odznaczony zostanie usunięty po zapisaniu. Usuwasz wyłącznie to, co widzisz na liście: przypisania z innej lokalizacji albo do zarchiwizowanego rodzaju zajęć zostają nietknięte.
- Zaznacz obecność i zapisz.
Kiedy poziom jest wymagany
Poziom musisz wskazać każdemu obecnemu uczestnikowi zajęć próbnych — bez tego przycisk zapisu jest nieaktywny, a nad nim pojawia się komunikat.
Uczestnik nieobecny na próbnych poziomu nie wymaga: nie było czego oceniać, a jeden nierozstrzygnięty przypadek nie może blokować zapisu obecności całej grupy.
Uczestnik, który ma już poziom przypisany, spełnia ten warunek — pole jest wypełnione i nie musisz nic zmieniać. Nie możesz natomiast wyczyścić pola obecnemu uczestnikowi do zera: zapis zostanie zablokowany, dopóki nie wskażesz przynajmniej jednego poziomu. Nieobecnemu wolno wyczyścić wszystko.
Ile masz czasu
Zwykłą obecność oznacza się do 35 minut od startu zajęć. Zajęcia, na których jest ktoś z próbnych, mają dłuższe okno — 24 godziny od zakończenia zajęć — bo poziom ocenia się w trakcie i pod koniec zajęć, a nie na starcie.
Z tego samego powodu zajęcia z uczestnikiem próbnym pokazują przycisk Sprawdź obecność nawet wtedy, gdy dany rodzaj zajęć ma wyłączone sprawdzanie obecności — inaczej nie byłoby jak nadać poziomu.
Pomyłka
Przypisania poprawia biuro: Dashboard ➔ Zadania albo karta uczestnika.
🛠 Dokumentacja techniczna
Model danych
Nie powstała żadna nowa tabela ani migracja. Poziom to rodzaj zajęć, więc
przypisanie ląduje w istniejącej tabeli player_activity_types
(UNIQUE(player_id, activity_type_id, tenant_id), kolumna assigned_by).
Uczestnik zajęć próbnych to wpis w JSON-ie game.attendees z
type: 'skill-assessment' — ten sam mechanizm, z którego korzysta obecność
(present).
Przepływ
Synchronizacja zamiast dopisywania
syncTrialLevels zestawia wybór trenera z tym, co jest w bazie, i wykonuje
różnicę: dodaje zaznaczone, usuwa odznaczone. Zakres usuwania jest ograniczony
do poziomów, które trener w ogóle widział — czyli aktywnych typów group z
miasta i ulicy kortu, wyliczonych po stronie serwera, a nie przysłanych przez
klienta. Dzięki temu:
- odznaczenie w UI faktycznie kasuje przypisanie (inaczej pole pokazywałoby stan, na który nie da się wpłynąć),
- przypisanie spoza tej listy — inne miasto, typ indywidualny, typ zarchiwizowany — nigdy nie zostanie skasowane, nawet gdyby klient je przysłał.
Z tego samego powodu getTrialLevelOptions zwraca assignments przefiltrowane
do widocznych typów: pole nie może pokazywać wartości, której nie ma na liście
wyboru.
Pliki
| Plik | Rola |
|---|---|
lib/actions/trial-level.ts | getTrialLevelOptions, syncTrialLevels, walidacja wymagalności |
lib/actions/game.ts → markAttendance | trzeci, opcjonalny argument trialLevels; zapisuje poziomy przed obecnością |
| app/api/games/[gameId]/trial-levels/route.ts | odczyt opcji dla dialogu (route handler, nie server action) |
| app/(dashboard)/dashboard/schedule/components/AttendanceChecker.tsx | pole Poziom na karcie uczestnika próbnego |
| components/schedule/activity-details-dialog.tsx | pobranie opcji, walidacja przed zapisem, okno czasowe |
| lib/utils/trial-level-selection.ts | stan pola budowany wyłącznie z zapisanych przypisań |
| settings.ts | HOURS_AFTER_GAME_END_TO_ASSIGN_TRIAL_LEVEL |
Lista poziomów
activity_types filtrowane po type = 'group', archived = 0 oraz mieście i
ulicy kortu, na którym odbywają się zajęcia. Filtrujemy po type, nie po
category = 'Szkółki' — kategoria to wolny tekst, który tenant może przemianować.
Autoryzacja
Akcje addPlayerActivityType / setPlayerActivityTypes chroniło dotąd wyłącznie
to, że jedynym wejściem była strona /dashboard/tasks ograniczona do
ADMIN/BACKOFFICE. Ścieżka trenera ma własne guardy, niezależne od dostępu do
strony:
- rola
INSTRUCTOR,ADMINlubBACKOFFICE canInstructorAccessGame()— przypisany trener albo zastępca tych zajęć- gracz musi być uczestnikiem tej gry z
type = 'skill-assessment' - rodzaj zajęć musi być aktywnym typem
grouptego samego tenanta
Walidacja wymagalności („każdy obecny próbny ma poziom") działa po stronie serwera, nie tylko w UI.
Kontrakt markAttendance
Trzeci argument rozstrzyga, czy wymagalność jest egzekwowana:
- pominięty (
undefined) — wywołanie sprzed tej zmiany, np. formularz edycji zajęć administratora (EditGameForm.tsx). Obecność zapisuje się jak dotąd, poziomów nie dotykamy i niczego nie wymagamy. Bez tego rozróżnienia obecny uczestnik próbny bez poziomu blokowałby administratorowi zapis w widoku, który nie ma pola poziomu. - tablica (również pusta) — wywołanie z widoku obecności trenera. Poziomy zapisujemy, a wymagalność jest sprawdzana.
Testy
lib/actions/trial-level.test.ts — 15 testów: filtrowanie listy poziomów,
domyślne zaznaczenie, dopisywanie bez kasowania, idempotencja, wymagalność przy
obecnym / brak wymagalności przy nieobecnym, odrzucenie gracza spoza zajęć,
typu spoza group i typu z innego tenanta.