Skip to main content

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?

  1. Wejdź w widok Grafik (kalendarz rezerwacji).
  2. W prawym górnym rogu, obok przycisku „Godziny otwarcia”, kliknij „Eksport”.
  3. 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.
  4. Kliknij „Generuj i kopiuj do schowka”. Raport zostanie utworzony i od razu skopiowany — pojawi się też jego podgląd.
  5. 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)?

SekcjaCo zawieraDo czego służy
PodsumowanieLiczba dni z zajęciami, godziny, uczestnicy, opłaceni, średnie zapełnienie, suma cenSzybki obraz skali w okresie
ZasobyLista 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ącychOkna 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 okresieAby nie planować zajęć na dni wolne
Serie cyklicznePowtarzalne serie zajęć (dzień, godzina, kort, trener, zapełnienie)Kręgosłup grafiku — to przekształca się na nowy sezon
Sygnały popytuTrend zapełnienia, sloty z kompletem miejsc, najpopularniejsze terminy, listy oczekującychGdzie jest niezaspokojony popyt i co warto rozbudować
ZestawieniaPodsumowania wg typu zajęć, kortu i dnia tygodniaPorównania i ranking popularności
Szczegóły wydarzeńTabela wszystkich wydarzeń, wiersz po wierszuPeł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):

  1. Na podstawie sekcji „Serie cykliczne” zaproponuj, które serie utrzymać bez zmian, a które przenieść na inny termin.
  2. Wykorzystaj „Sygnały popytu” — tam, gdzie zapełnienie było ≥ 90% lub była lista oczekujących, zaproponuj dodatkową grupę.
  3. Terminy o niskim zapełnieniu (< 40%) zaproponuj do połączenia lub likwidacji.
  4. Respektuj „Godziny otwarcia” kortów, „Dostępność prowadzących” i limity wiekowe z „Katalogu typów zajęć”.
  5. 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.tsexportCalendarData(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 (shadcn Calendar, tryb range), 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, popytgetGamesByCity(city, start, end, { includePayments: true })
Korty (nazwy, godziny otwarcia, wyłączenia)getCourtsGrouped()
Katalog typów zajęćgetAllActivityTypes()
Prowadzący (ID)getInstructors()
Dostępność prowadzącychgetAllInstructorsAvailabilityForDateRange(ids, start, end)
ŚwiętagetHolidays(city)
Listy oczekującychCOUNT 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, getHolidays oraz 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()).