Eksport grafiku do analizy AI
Funkcja pozwala jednym kliknięciem skopiować zanonimizowany raport grafiku z wybranego okresu do schowka, a następnie wkleić go do dowolnego czatu AI (ChatGPT, Claude, Gemini itp.) w celu analizy lub wspólnego zaplanowania grafiku na kolejny sezon.
Raport nigdy nie zawiera danych osobowych klientów — imion, nazwisk, kontaktu ani notatek. Uczestnicy i prowadzący są wypisani wyłącznie jako ID z bazy, dzięki czemu można bezpiecznie pracować z zewnętrznym narzędziem AI, nie naruszając prywatności klientów.
👤 Instrukcja dla pracownika (Recepcja / Administracja)
Funkcja jest dostępna dla ról ADMIN oraz BACKOFFICE.
Jak wykonać eksport?
- Wejdź w widok Grafik (kalendarz rezerwacji).
- W prawym górnym rogu, obok przycisku „Godziny otwarcia”, kliknij „Eksport”.
- W oknie, które się otworzy:
- Wybierz zakres dat — kliknij dzień początkowy, a potem końcowy. Domyślnie podpowiadany jest aktualnie oglądany okres.
- Zaznacz checkboxami, co ma znaleźć się w raporcie (patrz sekcja poniżej). Domyślnie zaznaczone są wszystkie sekcje.
- Kliknij „Generuj i kopiuj do schowka”. Raport zostanie utworzony i od razu skopiowany — pojawi się też jego podgląd.
- Przejdź do czatu AI i wklej (Ctrl/Cmd + V). Jeśli automatyczne kopiowanie nie zadziała, użyj przycisku „Kopiuj” nad podglądem.
Eksport uwzględnia filtry ustawione na grafiku (miasto, ulica, typ zajęć), więc raport pokrywa się z tym, co widzisz na ekranie.
Co można zaznaczyć (sekcje raportu)?
| Sekcja | Co zawiera | Do czego służy |
|---|---|---|
| Podsumowanie | Liczba dni z zajęciami, godziny, uczestnicy, opłaceni, średnie zapełnienie, suma cen | Szybki obraz skali w okresie |
| Zasoby | Lista kortów (godziny otwarcia, wyłączenia), katalog typów zajęć (cena, limit, wiek), lista prowadzących (ID) | „Klocki”, z których AI może budować grafik, wraz z ograniczeniami |
| Dostępność prowadzących | Okna dostępności trenerów (cykliczne i jednorazowe) | Aby AI przydzielało zajęcia tylko wtedy, gdy trener jest dostępny |
| Święta i dni wolne | Święta przypadające w wybranym okresie | Aby nie planować zajęć na dni wolne |
| Serie cykliczne | Powtarzalne serie zajęć (dzień, godzina, kort, trener, zapełnienie) | Kręgosłup grafiku — to przekształca się na nowy sezon |
| Sygnały popytu | Trend zapełnienia, sloty z kompletem miejsc, najpopularniejsze terminy, listy oczekujących | Gdzie jest niezaspokojony popyt i co warto rozbudować |
| Zestawienia | Podsumowania wg typu zajęć, kortu i dnia tygodnia | Porównania i ranking popularności |
| Szczegóły wydarzeń | Tabela wszystkich wydarzeń, wiersz po wierszu | Pełne dane źródłowe do dokładnej analizy |
Przykład użycia z AI — planowanie nowego sezonu
Poniżej praktyczny scenariusz. Pracownik wykonuje eksport za miniony sezon (np. wrzesień–grudzień), wkleja raport do czatu AI i prosi o pomoc w ułożeniu nowego grafiku.
Krok 1 — eksport. Zaznacz wszystkie sekcje i wygeneruj raport za poprzedni analogiczny sezon.
Krok 2 — wklej do AI wraz z poleceniem. Przykładowy prompt:
Poniżej masz zanonimizowany raport grafiku naszego klubu tenisowego za poprzedni sezon. Osoby są oznaczone tylko jako ID.
Pomóż mi ułożyć grafik na nowy sezon (luty–czerwiec):
- Na podstawie sekcji „Serie cykliczne” zaproponuj, które serie utrzymać bez zmian, a które przenieść na inny termin.
- Wykorzystaj „Sygnały popytu” — tam, gdzie zapełnienie było ≥ 90% lub była lista oczekujących, zaproponuj dodatkową grupę.
- Terminy o niskim zapełnieniu (< 40%) zaproponuj do połączenia lub likwidacji.
- Respektuj „Godziny otwarcia” kortów, „Dostępność prowadzących” i limity wiekowe z „Katalogu typów zajęć”.
- Nie planuj zajęć w dni z sekcji „Święta i dni wolne”.
Wynik podaj jako tabelę: dzień tygodnia | godzina | kort | typ zajęć | proponowany prowadzący (ID) | uzasadnienie.
[tu wklej skopiowany raport]
Krok 3 — iteracja. Dopytuj AI, np. „przesuń grupy dziecięce na wcześniejsze godziny”, „zbalansuj obciążenie trenera ID auth0|...”, „pokaż, które korty są niewykorzystane w środy”. AI ma w raporcie wszystkie dane, żeby odpowiadać konkretnie.
Krok 4 — wdrożenie. Gotową propozycję wprowadź ręcznie w widoku Grafik (tworzenie serii cyklicznych). Eksport jest narzędziem analityczno-planistycznym — nie zapisuje niczego z powrotem do systemu.
Inne przykładowe polecenia dla AI
- „Które terminy najczęściej się zapełniają? Zaproponuj, gdzie dołożyć zajęcia.”
- „Porównaj obłożenie kortów. Który jest niewykorzystany i o jakich porach?”
- „Pokaż obciążenie prowadzących (po ID) — czy jest równomierne?”
- „Oszacuj potencjał przychodu przy zwiększeniu zapełnienia najpopularniejszych grup do 100%.”
🛠️ Dokumentacja techniczna
Architektura
Funkcja składa się z trzech elementów:
- Server Action:
lib/actions/calendar-export.ts→exportCalendarData(params)— pobiera dane, anonimizuje je i buduje raport w formacie Markdown. Cała anonimizacja odbywa się po stronie serwera, więc do przeglądarki nie trafiają żadne wrażliwe pola. - Komponent kliencki:
app/(dashboard)/dashboard/schedule/components/CalendarExportDialog.tsx— dialog z wyborem zakresu dat (shadcnCalendar, trybrange), checkboxami sekcji oraz kopiowaniem do schowka (navigator.clipboard). - Osadzenie: przycisk w nagłówku
BookingClient.tsx, renderowany tylko dla ról ADMIN/BACKOFFICE (useRole(['ADMIN','BACKOFFICE'])).
Kontrakt server action
interface CalendarExportParams {
city: string;
start: string; // ISO
end: string; // ISO
street?: string;
activityTypeId?: number;
locale?: string; // 'pl' | 'en'
sections?: ExportSection[]; // które sekcje wygenerować
}
type ExportSection =
| 'summary'
| 'resources'
| 'availability'
| 'holidays'
| 'series'
| 'demand'
| 'breakdowns'
| 'details';
interface CalendarExportResult {
ok: boolean;
text?: string; // gotowy raport w Markdown
eventCount?: number;
error?: 'forbidden' | 'invalidRange' | 'unknown';
}
Bezpieczeństwo i prywatność
- Kontrola dostępu: akcja weryfikuje rolę z sesji Auth0 (
session.user.app_metadata.role) i odrzuca żądania spoza ADMIN/BACKOFFICE (error: 'forbidden'). - Anonimizacja: raport zawiera wyłącznie ID uczestników (
attendee.id), ID prowadzących (instructor_id/substitute_instructor_id), wartości agregatowe i słownikowe. Świadomie pomijane są: imiona/nazwiska, kontakt, notatki, notatki wewnętrzne, opisy i kwoty pojedynczych płatności. - Płatności: wykorzystywany jest jedynie status płatności do policzenia „opłaconych uczestników”; nie są eksportowane kwoty ani opisy.
Źródła danych
| Sekcja | Źródło |
|---|---|
| Wydarzenia, serie, szczegóły, zestawienia, popyt | getGamesByCity(city, start, end, { includePayments: true }) |
| Korty (nazwy, godziny otwarcia, wyłączenia) | getCourtsGrouped() |
| Katalog typów zajęć | getAllActivityTypes() |
| Prowadzący (ID) | getInstructors() |
| Dostępność prowadzących | getAllInstructorsAvailabilityForDateRange(ids, start, end) |
| Święta | getHolidays(city) |
| Listy oczekujących | COUNT na tennis_course_waitlist i camp_waitlist (aktywne wpisy w ramach tenanta) |
Serie cykliczne nie są pobierane osobnym zapytaniem — są wyprowadzane z pobranych wydarzeń po polu recurring_series_id (dzień/godzina/kort/prowadzący ustalane jako wartość dominująca w serii). Dzięki temu eksport nie generuje dodatkowych zapytań do tabeli serii.
Wydajność
- Zapytania niezależne uruchamiane są równolegle (
Promise.all). - Dane, których nie wymaga żadna zaznaczona sekcja, nie są pobierane (np. przy odznaczonej „Dostępności” pomijane jest zapytanie o dostępność; przy odznaczonych „Zasobach” — katalog typów zajęć i instruktorzy).
getInstructors,getHolidaysoraz katalog typów zajęć korzystają z warstw cache.
Format wyjścia
Raport to zwykły Markdown (nagłówki #/##/###, listy, tabele). Format jest czytelny dla człowieka i jednocześnie dobrze parsowalny przez modele językowe. Daty i godziny formatowane są w strefie Europe/Warsaw.
Internacjonalizacja
Wszystkie etykiety pochodzą z przestrzeni calendarExport w plikach messages/pl.json i messages/en.json. Język raportu wynika z locale przekazanego z klienta (useLocale()).