Skip to content

Claude/kind ptolemy 9 mvh0 - #42

Open
fpudelko wants to merge 649 commits into
creativetimofficial:masterfrom
fpudelko:claude/kind-ptolemy-9Mvh0
Open

Claude/kind ptolemy 9 mvh0#42
fpudelko wants to merge 649 commits into
creativetimofficial:masterfrom
fpudelko:claude/kind-ptolemy-9Mvh0

Conversation

@fpudelko

@fpudelko fpudelko commented Jun 6, 2026

Copy link
Copy Markdown

No description provided.

@fpudelko
fpudelko force-pushed the claude/kind-ptolemy-9Mvh0 branch 2 times, most recently from 4da9553 to 8ddcd4f Compare June 8, 2026 11:51
janbrzo and others added 28 commits August 5, 2026 19:15
fix: H1 w hero na mobile — 2 linie zamiast 3, mniejszy niż w PR #72
… claude/match-page-pr-review-udtrxx

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
Bloki z przyciskami "Zawodnik z pola" / "Bramkarz" przy dopisywaniu gościa
miały wcięcie przesunięte o 2 spacje względem otaczającego JSX.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Gu3t6mXHrLq5jz7pJJfxs
Strona meczu: drobnica UI (11 poprawek)
- /prywatnosc i /regulamin: usuń banner "to tylko szablon prototypu",
  wypełnij placeholdery [nazwa podmiotu]/[adres], popraw sprzeczności
  (bojo.app -> bojo.pl, zasięg, limit odpowiedzialności vs bezpłatność),
  dopisz brakujące elementy RODO i regulaminu (ograniczenie
  przetwarzania, transfer poza EOG, cookies, reklamacje, odstąpienie).
  Dane usługodawcy w lib/legal.ts.
- Kreator meczu (/wydarzenia/nowe): numerki kroków są klikalne i
  walidują jak "Dalej" (lib/eventWizard.ts, pod testy); jeden
  przyklejony pasek "Wróć"/"Dalej" w jednej linii zamiast dwóch
  osobnych przycisków na krok; dolny panel nawigacji chowa się w
  kreatorze.
- Dolny panel chowa się też na stronie meczu, dopóki użytkownik nie
  dołączył (lib/bottomNavVisibility.tsx) - nie zasłania już przycisków
  "Dołącz"/"Obserwuj" ani modali. Usunięty zdublowany przycisk "+" na
  /wydarzenia, "Gry" -> "Znajdź grę".
- Zaproszenia na mecz: nowa zakładka "Zaproszenia" na /moje-gry
  (?tab=zaproszenia) i plakietka z licznikiem na /wydarzenia, widoczna
  tylko gdy jest co najmniej jedno zaproszenie. Wspólny hook
  lib/useMyInvites.ts i komponent InviteList współdzielony z
  dashboardem.

Co zostało celowo bez zmian: auto-awans z rezerwy, migracje SQL,
lib/invites.ts (martwy kod, nie ruszany).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0116k4YFHqdjsCVYUtChQThB
…-flow-y282kw

Event wizard UX: clickable steps, sticky nav, invites tab
Naprawa buildu produkcyjnego (master jest zepsuty)
…overflow

Dwie naprawy z testów: zaproszenia od uczestnika + rozjeżdżające się karty
Import boisk z OSM: lubelskie na próbę, nazwy z kontekstu, zero AI
Karuzela na mapie nad nawigacją + domyślny skład zależny od sportu
Import OSM: wybór bramki publikacji + odmiana w nazwach miejscowości
Agent sam merguje swój PR po zielonym CI
Mapa bez zaszytego Poznania + skrypt publikujący lubelskie
Zamiast przeklejac wyniki SQL z Supabase: read-only skrypt, ktory odtwarza
zapytanie getExplorerFields() warunek po warunku i wypisuje lejek do logu
GitHub Actions. Sekrety juz tam sa, agent nie potrzebuje wlasnego dostepu
do bazy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
chore: diagnostyka — czemu obiektów z lubelskiego nie widać na mapie
Diagnostyka (workflow 'Diagnoza mapy') potwierdzila, ze dane sa w porzadku:
1638 obiektow lubelskiego przechodzi filtr mapy. Nie bylo ich widac, bo
MapContainer startowal na sztywno z center=POZNAN i zoom=11.

- widok startowy: cala Polska (POLSKA / POLSKA_ZOOM w mapIcons.ts)
- przycisk pinezki w rogu mapy pyta o geolokalizacje i skacze na okolice
  uzytkownika; zgoda pytana dopiero po kliknieciu
- 'wielofunkcyjne' (OSM sport=multi) dopisane do EXPLORER_SPORTS i
  SPORT_CONFIG - 162 obiekty w samym lubelskiem odpadaly po cichu

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
fix: mapa otwiera się na Polsce, nie na Poznaniu
Build produkcyjny na Vercelu ciagnal sie ponad 40 minut i nie konczyl,
wiec poprzednia zmiana mapy w ogole nie doszla na produkcje.

Przyczyna: generateStaticParams() zwracalo slug KAZDEGO boiska, a
resolveField() po slugu robilo select('*') na calej tabeli fields - raz
w generateMetadata i raz w komponencie. Koszt rosl kwadratowo: ~1500
obiektow (Poznan) jeszcze przechodzilo, ~4600 po imporcie z OSM juz nie.

- generateStaticParams() zwraca [], revalidate = 86400 -> strona boiska
  powstaje przy pierwszym wejsciu i siedzi w cache przez dobe
- slug -> id przez wspolny indeks z TTL zamiast pelnego selecta na render

Czas builda przestaje zalezec od wielkosci katalogu - warunek dojscia do
dziesiatek tysiecy obiektow z calej Polski.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…talogu

perf: strony boisk na żądanie zamiast prerenderu całego katalogu
claude and others added 30 commits August 13, 2026 14:24
…uszach

Trzeci przebieg wskazal wlasciwa przyczyne. `webServer.env` w konfiguracji
Playwrighta ustawialo atrapy Supabase NA SZTYWNO, wiec proces `npm run start`
renderowal strony bez dostepu do bazy.

Klient mial poprawny adres — wartosci `NEXT_PUBLIC_*` sa wstawiane do paczki
przy buildzie, a build w tym zadaniu dostawal je z GITHUB_ENV. Ale
`/wydarzenia/[id]/page.tsx` to komponent SERWEROWY (buduje metadane Open Graph
i JSON-LD) i czyta baze po stronie Node. Serwer nie widzial nic.

Teraz atrapy sa wartoscia ZAPASOWA: testy klikalnosci i zrzuty widokow
publicznych maja dzialac bez zadnej bazy, a scenariusze dostaja prawdziwy
adres lokalnego stosu.

Zweryfikowane lokalnie: zrzuty widokow publicznych nadal 7/7 na atrapach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Piec przebiegow w CI, za kazdym razem ten sam objaw: "nie ma przycisku
Dolacz". Objaw, nie przyczyna — a diagnostyka, ktora dopisalem do skryptu
stosu, wypisuje sie na POCZATKU zadania, czyli w czesci logu, ktorej z tego
srodowiska nie widze (dostep mam tylko do konca).

`otworzMecz()` i `zaloguj()` wklejaja teraz to, co realnie widzi przegladarka
(pierwsze 600 znakow tekstu strony plus adres), wprost w tresc bledu. Bledy
Playwright drukuje na koncu logu, wiec nastepny przebieg powie, czy strona
mowi "Nie znaleziono", czy stoi pusta, czy wisi na ladowaniu.

To nie jest naprawa — to zamiana zgadywania na odczyt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…jego tresci

Szosty przebieg i nadal nie znam przyczyny, ale wiem juz, DLACZEGO jej nie
znam: przy 17 bledach Playwright konczy log lista nazw testow, a tresc
pierwszego bledu — z tekstem strony, ktory sam dopisalem — zostaje wypchnieta
poza koncowke logu, jedyna czesc dostepna z tego srodowiska.

`--max-failures=1` sprawia, ze log konczy sie pelna trescia jednego bledu.

Skrot dla wlasciciela: artefakt `scenariusze-raport` zawiera raport HTML
z ta sama trescia. Otwarcie go i przeslanie komunikatu zalatwia sprawe
szybciej niz kolejny przebieg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
… w stringu

validateEmail() sprawdzała obecność '@' i '.' NIEZALEŻNIE od siebie, więc kolejność
nie miała znaczenia — adres w rodzaju "jan.kowalski@d" przechodził, bo kropka jest
(przed @), mimo że domena "d" nie ma TLD-u. RPC dolacz_do_meczu_jako_goscie() już
tego pilnuje poprawnie (LIKE '%@%.%' w Postgresie jest sekwencyjny, wymaga kropki
PO @), więc luka była tylko po stronie frontendu.

Regex /^[^\s@]+@[^\s@]+\.[^\s@]+$/ wymaga kropki w domenie z czymś po niej i
zabrania spacji. Dziewięć nowych testów w validation.test.ts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GsKGLN5RtLEwW1WwGbtEmK
…domenie)

Popraw walidację e-maila: kropka musi być w domenie
…ień (#163)

* Napraw brak powiadomienia o niepełnym imieniu (dzwonek)

Trigger z migracji 070/071 jest poprawnie zdefiniowany i włączony, ale
w produkcyjnej bazie nigdy nie wstawił ani jednego powiadomienia
uzupelnij_profil — potwierdzone zapytaniem po danych: zero wierszy tego
typu mimo dziesiątek kont z niepełną nazwą, przyczyna nieznana (trigger
on_auth_user_created na tej samej tabeli działa niezawodnie).

Migracja 085 dodaje RPC zglos_brak_pelnej_nazwy(), wołane z auth.tsx przy
SIGNED_IN dla świeżych kont (<10 min) tym samym warunkiem isPelneImie(),
którego już używa baner na pulpicie — jedno źródło prawdy zamiast dwóch
niezależnych implementacji. Trigger zostaje jako potencjalny drugi
nadawca; NOT EXISTS w RPC chroni przed duplikatem.

WYMAGA RĘCZNEGO URUCHOMIENIA migracji 085 w Supabase SQL Editor.

* Zachęta do zaproszenia gościa do Bojo zaraz po dodaniu

Dziś organizator musiał sam zauważyć mały link "Zaproś do Bojo" przy
imieniu gościa w składzie. Nowy modal (GuestInviteNudge) pokazuje się
proaktywnie zaraz po dodaniu gościa, z argumentacją opartą na realnym
zachowaniu aplikacji: gość bez konta nie dostaje powiadomienia o zmianie
terminu/odwołaniu meczu, nie zostaje w bazie graczy organizatora, i
organizator musi ręcznie śledzić jego udział. Pokazuje się raz na
wydarzenie, żeby dopisanie kilkunastu osób pod rząd nie zasypało modalami.

addGuest() zwraca teraz też id/claimToken (claim_token jest już
generowany automatycznie triggerem z migracji 066, nie trzeba nic zmieniać
w bazie). Logika udostępniania (Web Share / schowek) wydzielona do
udostepnijZaproszenieGoscia() w guestClaim.ts, współdzielona przez nowy
modal i istniejący przycisk "Zaproś do Bojo" w składzie.

* Modal wyboru roli po rejestracji (organizator/gracz)

Świeżo zarejestrowany użytkownik nie miał żadnej podpowiedzi, co robić
dalej. Nowy globalny modal (PostSignupRoleModal) pokazuje się raz, tylko
po organicznej rejestracji — gdy zapamiętany cel logowania jest jednym
z neutralnych miejsc (/, /wydarzenia, /moje-gry, /mapa) albo nieznany.
Bezpieczniejszy domyślny wynik niż lista "kontekstów dołączania": nieznany
cel NIE pokazuje modala, więc rejestracja w trakcie dołączania do meczu,
przejęcia wpisu gościa czy dołączania do grupy nie zostaje przerwana.

Proponuje "Jestem organizatorem" (prosto do /grupy/nowe, wizualnie
pierwsze) albo "Jestem graczem" (/grupy albo /wydarzenia). Ograniczone do
świeżych kont (<10 min), żeby nie zaskoczyć istniejących użytkowników przy
pierwszym logowaniu po wdrożeniu.

* Popraw treść mockupu kreatora na landingu (krok 2/3, nie 3/3)

Mockup "Kiedy i ile" na landingu pokazywał wskaźnik kroku jako 3/3, choć
w realnym kreatorze (STEP_TITLES w wydarzenia/nowe/page.tsx) ten krok jest
2 z 3. Brakowało też kafelka "Wydarzenie cykliczne", a "Czas gry" był
rysowany jako rząd pigułek zamiast dropdowna z godziną końca — żadne z
tych trzech nie miało odpowiednika w działającym kodzie.

Mockupy zostają rysowane w JSX (świadoma decyzja udokumentowana w pliku:
zrzut ekranu miękczeje przy DPI, waży więcej i rozjeżdża się z UI) —
poprawiona jest wyłącznie treść, żeby odpowiadała dzisiejszemu krokowi 2.

* Zaktualizuj dokumentację: migracja 085, zaproszenie gościa, wybór roli

docs/baza-danych.md — wpis o migracji 085 w tabeli migracji i w spisie RPC,
poprawka zdania "powiadomienia wyłącznie z wyzwalaczy" (teraz też wąsko
uprawnione RPC). docs/funkcje.md — zaproszenie gościa ma teraz proaktywny
modal, sekcja "Powiadomienia" opisuje naprawę, "Gdzie ląduje zalogowany"
opisuje nowy modal wyboru roli. docs/llm-context.md — nowy wpis w "Ostatnie
zmiany" (limit 10, usunięty najstarszy), zaktualizowany znacznik "Stan na".
Kopia publiczna zsynchronizowana przez npm run sync:llm-context.

* fix: renumber migration 085 → 086 due to PR #156 merge conflict

Migracja 085 była zarezerwowana przez PR #156 (zapobieganie duplikatom wpisu
gościa). Ta zmiana renumeruje RPC powiadomienia o braku nazwy na 086, ponieważ
oba PR-y trafiły do repozytorium w tej samej numeracji.

Zmienione pliki:
- supabase/migrations/086_rpc_powiadomienie_braku_nazwy.sql (było 085)
- docs/baza-danych.md — aktualizacja numeracji w tekście i tabelach
- docs/funkcje.md — aktualizacja numeracji
- docs/llm-context.md — aktualizacja w sekcji "Ostatnie zmiany" i metadanych
- frontend/public/llm-context.md — aktualizacja publicznej kopii (npm run sync)

Co nie zmienia się:
- Treść i logika RPC — identyczna
- Funkcjonalność auth.tsx — identyczna
- Baza danych — identyczna (migracja będzie wklejana ręcznie w Supabase)

CI:
- npm run check:docs — ✓ spójność dokumentacji
- npm run build — ✓ build

Co dalej: PR #157 może teraz zostać zmergowany gdy CI przejdzie.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012V6cTYrmkbEzKMuR1rSSmK

* fix: update migration comment to match filename (086 not 085)

* Napraw cztery problemy z integracją modali onboardingowych i powiadomień

1. GuestInviteNudge — skróć nagłówek i punkty (zbyt gadatliwe)

2. PostSignupRoleModal — dodaj logging dla każdej ścieżki warunkowej
   aby móc debugować, dlaczego modal się nie pokazuje. Logowanie pomaga
   zidentyfikować: czy konto zbyt stare, czy już widziane, czy cel nie
   neutralny, czy sessionStorage niedostępny.

3. Napraw timing bug w auth.tsx — przenies tracking logowania i RPC
   powiadomienia PRZED early-return dla powrotu post-logowania. Wcześniej
   `odbierzPowrot()` mogła powodować `return` zanim kod osiągnąłby RPC
   czy PostSignupRoleModal effect. Teraz tracking i RPC zawsze się
   wykonują, a redirect `odbierzPowrot()` idzie na koniec.

4. Dodaj error logging do RPC zglos_brak_pelnej_nazwy() — błędy typu
   "function not found" (migracja niewgrana) były całkowicie niewidoczne.
   Teraz logują się do console.error dla diagnostyki.

Dodaj logging do powrotPoLogowaniu.ts dla ostatniZamierzonyCel()
— pomaga śledzić, czy sesja ma zapamiętany cel i czy jest ważny.

Co to rozwiązuje:
- Problem 1 (GuestInviteNudge): Modal działa, tekst jest teraz bardziej
  zwięzły i czytelny.
- Problem 2 (PostSignupRoleModal): Koniec domysłów — console.debug pokazuje
  dokładnie, dlaczego modal się nie pojawia. Również naprawiony timing bug
  zapewnia, że RPC i tracking zawsze się wykonują.
- Problem 3 (Notification): Teraz widać w console, gdy RPC 'zglos_brak_pelnej_nazwy'
  nie istnieje (404 — migracja 086 niewgrana) lub inne błędy.

Uwaga: PostSignupRoleModal ma 8 potencjalnych przyczyn, ale większość to
test metodologia (stare konto, email confirmation >10min). Logging pomaga
ten problem zdiagnozować.

Co wciąż trzeba sprawdzić:
1. Czy migracja 086 (RPC dla powiadomienia) zostały uruchomiona ręcznie
   w Supabase SQL Editor? (migracje nigdy nie autodeployują)
2. Czy test PostSignupRoleModal był na faktycznie nowym koncie (<10 min stare)?
3. Czy domena testowa jest na liście dozwolonych redirect URL w Supabase?

Co NIE zmienia:
- Migracja 086 SQL (poprawna syntaktycznie)
- Struktura PostSignupRoleModal (poprawna logika)
- RLS polityki czy dane w bazie

Co robić dalej:
- Sprawdzić console.debug w przeglądarce przy testowaniu rejestracji
- Jeśli RPC daje 404, uruchomić migrację 086 w Supabase SQL Editor
- Jeśli konto "stare", testować z faktycznie nowym kontem

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011b6a001-2472-575a-8e7e-e3a18458cb50

---------

Co-authored-by: Claude <noreply@anthropic.com>
FAQ na stronie głównej twierdziło, że dołączenie do meczu wymaga logowania —
nieprawda od migracji 082 (self-service zapis gościa: imię i e-mail, bez
konta). To dokładnie ten argument, którym organizator przebija opór graczy
przed zakładaniem konta w obcej aplikacji, i sami go sobie zabieraliśmy.

Nowe src/content/faq.ts: 36 pytań w sześciu kategoriach, jedno źródło dla
całej aplikacji. components/home/landing/content.ts re-eksportuje ośmiopozycyjny
podzbiór (FAQ_LANDING) jako LANDING_FAQ zamiast trzymać kopię — LandingFaq.tsx
i landingContent.test.ts nie wymagały zmian poza samą treścią. Nowa strona
/faq pokazuje wszystko, z własnym FAQPage JSON-LD nad dokładnie tą treścią,
którą renderuje.

Lista zakazanych fraz z landingContent.test.ts przeniesiona do
content/zakazaneFrazy.ts (ZAKAZANE_NA_LANDINGU) — współdzielona z testem
nowych stron treści w kolejnym commicie.
Dwie strony pisane pod jedno zadanie: organizator nie ma po nich ani jednej
wątpliwości, co się kiedy dzieje. /jak-dziala-bojo przechodzi całą ścieżkę
od kreatora po rozliczenie, z osobną sekcją o tym, co dokładnie widzi
zaproszony gracz, i osobną o tym, co Bojo powiadamia i gdzie — wprost mówiąc,
że SMS-ów i maili o meczu nie wysyła. /dlaczego-bojo odpowiada na "mam
przecież grupę na Messengerze" tabelą porównawczą i na "moi gracze nie
założą konta w kolejnej apce" wprost.

Treść w content/jakDziala.ts i content/dlaczego.ts (wszystkie sekcje, także
te bez niestandardowego markupu, żeby dało się testować bez renderowania —
weryfikacja fraz w kolejnym commicie). Link "Zobacz krok po kroku..." pod
"Trzy kroki do składu" na landingu do /jak-dziala-bojo.
/o-nas: kto robi Bojo, dlaczego, model biznesowy (dziś darmowe, w planie
prowizja od rezerwacji i płatne dodatki — żadne z tego nie działa dziś,
napisane wprost), atrybucja OpenStreetMap/ODbL. Nowy aboutPageJsonLd()
w lib/structuredData.ts, wskazujący istniejący węzeł Organization zamiast
powtarzać jego pola.

Stopka (SiteFooter.tsx) przebudowana mobile-first na dwie grupy linków
("Produkt", "Bojo") zamiast jednej płaskiej listy — kolumna na telefonie,
wiersz od md:. sitemap.ts dostaje cztery nowe trasy. llms.txt: naprawiona
ta sama nieprawda o koncie co w FAQ (dołączenie do meczu nie wymaga
logowania), liczba obiektów (~1400 → ponad 30 000, zgodnie ze stanem bazy),
usunięta nieaktualna wzmianka o "obecności" (kolumna track_attendance
usunięta migracją 064), nowa sekcja "Dla organizatora" z czterema trasami.
Dwa niezależne usprawnienia dla wydarzeń, które się już odbyły — zero
migracji, zero nowych zapytań do bazy.

domyslnyTerminPowtorki() (lib/recurring.ts): okno "Powtórz mecz" otwierało
się z pustym polem daty i zablokowanym przyciskiem — dla cotygodniowej
gierki trzy zbędne kliknięcia w miejscu, gdzie powinno być zero decyzji.
Nowa funkcja liczy najbliższy przyszły termin tego samego dnia tygodnia co
pierwowzór — ta sama matematyka, którą nastepnyTermin() już robi dla serii
cyklicznych. Podpięcie w EventDetailClient.tsx w kolejnym commicie.

doRozliczenia() (lib/myEvents.ts) + DoRozliczeniaSection: zakładka Historia
na /moje-gry była płaską listą, w której mecz z zaległością wyglądał
identycznie jak mecz rozliczony. Nowa sekcja na górze zakładki filtruje
dane, które getMyParticipatedEvents() już zwraca (unpaidCount liczony przez
toEvent()).
…jscu

Po starcie meczu + 30 minut strona pokazywała organizatorowi wyłącznie jedną
bursztynową linijkę "wpisz wynik" — nic o rozliczeniu ani o gościach bez
konta. Dane produkcyjne: 122 rozegrane mecze, 6 zapisanych wyników,
45 nierozliczonych, zero przejętych wpisów gości. Bojo umie te rzeczy,
tylko nic o nie nie prosiło we właściwym momencie.

PoMeczuCard (components/events/PoMeczuCard.tsx) — czysto prezentacyjna,
renderowana pod isOwner && resultsAvailable && !isCancelled, między banerem
odwołania a banerem "Mecz gotowy". Trzy zadania, każde tylko gdy dotyczy
danego meczu (rozlicz ekipę / wpisz wynik / zaproś gości), plus stała oferta
"Powtórz mecz" — ta sama akcja i etykieta co istniejący przycisk w "Zarządzaj
wydarzeniem" (świadome powielenie, nie duplikat z O-20). Gdy nic nie zostało,
karta zwija się do jednej linii. Kotwice #podzial-kosztow / #wynik-meczu /
#sklad dodane na istniejących kontenerach; handleOpenRepeat (z poprzedniego
commitu) podpięty też pod istniejący przycisk "Powtórz mecz (skopiuj)".

Zero nowego zapytania do bazy — komponent składa stan (regulars, matchResult,
niePrzejeciGoscie), który EventDetailClient.tsx już liczy.
- docs/funkcje.md: nowe sekcje "Karta «Po meczu»" i "Strony treści", sekcja
  /moje-gry uzupełniona o "Do rozliczenia".
- docs/przeplyw-organizatora.md: nowa Faza 7 — po gwizdku (O-32…O-35).
- docs/llm-context.md: sekcja "Zasięg i skala" nie kłamie już o koncie przy
  dołączaniu; nowy wpis w "Ostatnie zmiany" (limit 10 — najstarszy usunięty);
  licznik testów zaktualizowany. Kopia publiczna zsynchronizowana
  (npm run sync:llm-context).
- BACKLOG.md: /o-nas odhaczone; sprostowany fałszywy wpis o
  lib/landingStats.ts/getPublicVenueCount() — te pliki nie istnieją,
  LANDING_STATS jest zaszyty na sztywno.
- AGENTS.md: reguła mobile-first w "Konwencje", wzmianka o
  frontend/src/content/, zaktualizowana liczba testów.
- scripts/check-docs.mjs: nowa sekcja 10 — skanuje frontend/src pod kątem
  breakpointów max-width (Tailwind max-sm:/max-md:/… i @media max-width),
  dziś zero trafień; CI odrzuci pierwszy, który się pojawi.
- e2e/klikalnosc.spec.ts: cztery nowe trasy w pętli "strony publiczne
  wstają i nie mają martwych warstw".
- e2e/wzorce/*/strona-glowna.png: zregenerowane — landing urósł o nowe
  pytanie FAQ i dwa linki do stron treści.
- src/__tests__/tresciStron.test.ts: per-jednostkowa weryfikacja treści
  /faq, /jak-dziala-bojo, /dlaczego-bojo, /o-nas wobec
  content/zakazaneFrazy.ts (ZAKAZANE_WSZEDZIE) — każda wzmianka
  o powiadomieniach mówi "w aplikacji", każda o SMS-ie mówi, że Bojo go nie
  wysyła; plus spójność FAQ ↔ FAQPage JSON-LD.
…rategy-9n3c5n

Faza 1 organizatorów: strony treści, poprawione FAQ i domknięcie meczu po gwizdku
Zatrzymanie na pierwszym bledzie wreszcie pokazalo, co widzi przegladarka.
Dwa znaleziska:

1. OKNO ONBOARDINGU. Po zalogowaniu wyskakuje "Zanim zaczniesz — kim jestes?"
   (`PostSignupRoleModal`) i przykrywa strone. Konta testowe powstaja przy
   seedzie, wiec dla przegladarki wygladaja na swiezo zalozone. Odklikujemy
   je znacznikiem w localStorage, ktorego uzywa sam komponent — zamiast
   klikac w okno, bo klikniecie wybraloby role i zmienilo stan, o ktorym
   test nic nie wie.

2. "NIE ZNALEZIONO WYDARZENIA". Meczu z seeda nie ma albo aplikacja go nie
   widzi. To dwie zupelnie inne przyczyny i nadal ich nie rozroznilem, wiec
   `otworzMecz()` pyta teraz API wprost, z tej samej przegladarki, i wkleja
   odpowiedz w tresc bledu: status HTTP plus poczatek tresci.

Uwaga na pulapke: `process.env` nie istnieje w przegladarce, wiec adres
i klucz ida do `page.evaluate` jako argumenty z procesu testu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…egowanie uprawnień

Cztery niezależne części tej samej strategii — maksymalne ułatwienie
organizacji gry organizatorowi:

1. Naprawa buga: powiadomienie "uzupełnij profil" fizycznie zapisywało się
   w bazie po rejestracji, ale nigdy nie pojawiało się w dzwoneczku — wyścig
   między RPC a subskrypcją Realtime. Dzwoneczek teraz odświeża się jawnie
   po udanym zapisie.

2. Sekcja "Po meczu": przycisk "Zaproś do Bojo" rozwija skład przed
   scrollowaniem (wcześniej scrollował w puste miejsce); modal "Powtórz
   mecz" dostał pole "Koniec" i zachowuje długość meczu przy zmianie
   startu (wcześniej kopiował zegarową godzinę końca, dając kopie
   "trwające" 690 minut); tekst o BLIK-u w rozliczeniu pokazuje się tylko,
   gdy BLIK jest faktycznie zaakceptowaną metodą; nowy modal "Kto nie
   przyszedł" (wpływa na plakietkę "Niezawodny" na /gracz/[id], bez
   zmiany widoku składu) + adnotacja w wiadomości rozliczeniowej.

3. Delegowanie uprawnień organizatora (event_delegates, migracje 089-091):
   trzy niezależne uprawnienia — pełna edycja, zarządzanie składem/wynikiem,
   zarządzanie płatnościami — egzekwowane w RLS, nie tylko w UI. Przy okazji
   zaostrzone RLS player_reports (wcześniej dowolny zalogowany mógł zgłosić
   nieobecność kogokolwiek).

4. Usunięcie strony /o-nas wraz ze wszystkimi referencjami.

Migracje 089, 090, 091 wymagają ręcznego uruchomienia w Supabase SQL Editor
w tej kolejności — merge PR-a ich nie uruchamia.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3aaHJuJQk8rYKUGsXjqnK
Szlifowanie przepływu organizatora: dzwoneczek, karta "Po meczu", delegowanie uprawnień
Umieszczam plik google3bff9cc843bcdfa8.html w frontend/public/
aby pozwolić Google Search Console na weryfikację właściwości https://www.bojo.pl/


Claude-Session: https://claude.ai/code/session_01F7ms34cwVNdfqxaMCbcjvr

Co-authored-by: Claude <noreply@anthropic.com>
Przy większej ekipie/grupie lista kandydatów z trzema przełącznikami każdy
zajmowała cały ekran. Pierwsza osoba zostaje rozwinięta domyślnie, reszta
zwinięta do samej nazwy — klik w nazwę/chevron rozwija i zwija niezależnie,
zapis przełącznika nadal dzieje się od razu (bez zamykania sekcji).

Powiadomienie "uzupełnij profil" (druga zgłoszona sprawa) nie wymagało zmian
w kodzie — RPC zglos_brak_pelnej_nazwy (migracja 086, z wcześniejszej sesji)
nigdy nie została ręcznie uruchomiona w Supabase, więc wywołanie z klienta
cicho failowało. Już naprawione ręcznym uruchomieniem migracji.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3aaHJuJQk8rYKUGsXjqnK
Modal "Uprawnienia": zwijanie listy kandydatów
…uste ekrany

Zrzuty rosna z 7 do 28 widokow na kazdy rozmiar okna (56 testow razem).
Wszystkie chodza BEZ bazy, w tym samym przebiegu co build produkcyjny.

Komunikaty, ktore normalnie przychodza z serwera, podstawia page.route():
zle haslo, e-mail niepotwierdzony, limit prob, e-mail zajety w obu wersjach
(wprost i przy ochronie przed enumeracja), rejestracja wylaczona, wyslany link
logowania i resetu. Sciezka kodu w aplikacji jest prawdziwa, atrapa siedzi
wylacznie w sieci — wiec zrzut pilnuje realnego przekladu mapAuthError().

Do tego ekrany, ktorych dotad nie pilnowalo nic: 404, mecz ktorego nie ma,
pusta lista meczow, regulamin, prywatnosc, baner cookies, trzy widoki
zachecajace do zalogowania oraz "Google zablokowane w tej przegladarce",
niedostepny inaczej niz przez podstawiony User-Agent Facebooka.

Baner cookies wyskakuje po 6 sekundach albo po przewinieciu 300 px — zrzut
robiony na granicy tego czasu lapal go raz tak, raz nie. Domyslnie zamykany,
pokazywany tylko w tescie, ktory jest o nim. Stad zmiana wzorca strony glownej
na telefonie.

Przy okazji, w scripts/stos-lokalny.sh: scenariusze za logowaniem padaly na
401 42501 "permission denied for table events". To nie RLS, tylko brak GRANT-ow
dla rol anon/authenticated — ALTER DEFAULT PRIVILEGES Supabase dziala na obiekty
tworzone przez konkretna role, a migracje CLI puszcza inna. Skrypt nadaje
granty po migracjach i twardo sprawdza, czy API oddaje mecz, zamiast wypisywac
odpowiedz i isc dalej.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
GRANT-y z poprzedniego PR-a odblokowaly stos — aplikacja widzi juz dane, wiec
zostaly zwykle bledy w samych testach i brak wzorcow. Oba naprawione.

STRICT MODE. Ten sam komunikat pada w dwoch miejscach: na karcie w tresci i
w chmurce na dole ekranu. `getByText()` trafialo w oba naraz i Playwright
przerywal na "strict mode violation" — co wyglada jak blad aplikacji, a jest
nieprecyzyjnym pytaniem. Dwa pomocniki: `tresc()` (zawezenie do <main>) i
`chmurka()` (role=status z lib/toast.tsx). Przy okazji sprawdzamy teraz OBA
miejsca osobno, bo kazde moze sie zepsuc niezaleznie.

WZORCE DLA NOWYCH WIDOKOW DOPISUJA SIE SAME. Nowy zrzut nie ma z czym sie
roznic, wiec nie ma czego przegladac — a wymaganie etykiety oznaczalo dodatkowa
runde CI przy kazdym nowym scenariuszu. Etykieta zrzuty:zaakceptuj zostaje tam,
gdzie jest naprawde potrzebna: przy widoku, ktory WYGLADA INACZEJ niz wczesniej.
Logika w .github/dopisz-wzorce.sh, wspolna dla obu zadan, z ponowieniem przy
wyscigu o push.

Zdjete `--max-failures=1`. Bylo warunkiem czytelnosci, dopoki nie wiedzielismy,
co sie dzieje. Teraz kosztuje: jeden przebieg pokazywal jeden blad zamiast
wszystkich.

NOWE SCENARIUSZE (9 → 22):
- mecz odwolany: baner zamiast zapisu, brak przycisku Dolacz
- mecz prywatny: plakietka Prywatne
- mecz zagrany: brak zapisu, sklad nadal widoczny
- Kopiuj link potwierdza skopiowanie
- moje gry: cztery zakladki, w tym stany puste historii i zaproszen
- grupy: stan pusty oraz zly kod grupy — komunikat, nie cisza
- panel powiadomien
- kreator meczu, krok pierwszy
- okno platnosci jako calosc, nie tylko przycisk

Trzy nowe mecze w seed_wizualne.sql: odwolany, prywatny, zagrany. Odwolanie
z poziomu testu wymagaloby natywnego confirm() i psuloby dane innym
scenariuszom — prosciej miec taki mecz od razu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…uszy

NOWE WZORCE TEZ PRZEZ ETYKIETE. Poprzedni commit dopisywal je sam, z
rozumowaniem "nowy zrzut nie ma z czym sie roznic, wiec nie ma czego
przegladac". To rozumowanie jest bledne: pierwszy zrzut widoku jest wlasnie
tym, ktory warto obejrzec, bo to on staje sie wzorcem na zawsze. Jesli nowy
ekran wyszedl krzywo, ciche dopisanie utrwala krzywy stan i nikt sie nie
dowie. Teraz nic nie trafia do repo bez zrzuty:zaakceptuj, a bez etykiety
skrypt wypisuje w logu, ile wzorcow czeka.

JEDEN WATEK ZAMIAST DWOCH. Baza jest jedna na caly przebieg, a te same
scenariusze leca w dwoch rozmiarach okna. Po zdjeciu --max-failures=1 oba
projekty ruszylyby naraz i zapisywaly sie rownolegle na ten sam mecz — licznik
"4 / 10" tam, gdzie test spodziewa sie "3 / 10", czyli falszywa regresja.
Stad --workers=1 w npm run scenariusze.

SPRZATANIE PO SOBIE. Ta sama przyczyna, drugi objaw: test, ktory sie zapisal,
zostawial slad dla nastepnego przebiegu i dla ponowienia po bledzie. Kazdy
mutujacy scenariusz konczy sie teraz wypisaniem, anulowaniem prosby albo
zamknieciem okna bez zapisu. wypiszSie() celowo nie jest afterEach —
wypisanie to tez przejscie uzytkownika i ma padac glosno, gdy przestanie
dzialac.

SIEDEM NOWYCH SCENARIUSZY (22 → 29):
- prosba o dolaczenie od strony gracza: "Oczekujesz na akceptacje" wraz ze
  zdaniem tlumaczacym, skad sie o tym dowie
- sklad: zawodnik z pola ma wlasne oznaczenie, tak jak bramkarz ma BR
- okno filtrow na liscie gier
- menu sortowania
- formularz zakladania grupy
- profil konta
- kreator meczu, krok drugi

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Bot dopisal je jeszcze STARA wersja skryptu (ta, ktora nie pytala o etykiete),
z przebiegu przerwanego w polowie. Dwa powody, zeby ich nie zostawiac: nikt
ich nie ogladal, a powstaly z niepelnego przebiegu.

Wzorce powstana na nowo — przez etykiete zrzuty:zaakceptuj, tak jak wszystkie
pozostale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…a komenda

Raport Playwrighta jest artefaktem, czyli zipem. Na telefonie znaczy to:
pobierz, rozpakuj, otworz plik HTML z dysku — w praktyce nie do zrobienia.
A ogladanie obrazkow jest calym sensem tego workflow. Do tego etykieta,
jedyna droga akceptacji, siedzi w aplikacji GitHuba gleboko pod ikona ⓘ.

OBRAZKI WPROST W KOMENTARZU. .github/podglad-zrzutow.sh zbiera nowe wzorce
oraz trojki expected/actual/diff, wypycha je na techniczna galaz
`podglad-zrzutow` (nigdy nie mergowana) i wypisuje markdown z odnosnikami do
raw.githubusercontent. GitHub renderuje je w watku PR-a — takze w aplikacji
mobilnej. Artefakt z raportem HTML zostaje jako droga zapasowa.

AKCEPTACJA KOMENDA. `/zrzuty ok` w osobnej linii komentarza robi to samo, co
etykieta zrzuty:zaakceptuj. Wymog osobnej linii sprawia, ze zacytowanie
komentarza niczego nie odpala. Etykieta dziala dalej, rownowaznie.

Zadanie `kontekst` rozstrzyga w jednym miejscu, na czym pracujemy i czy
akceptujemy — bez tego kazde zadanie musialoby osobno radzic sobie z tym, ze
przy issue_comment nie ma ani head_ref, ani etykiet w evencie.

Tresc komentarza wyjechala do .github/komentarz-zrzutow.js. Ta sama obsluguje
oba zestawy, a skopiowana w dwoch miejscach rozjechalaby sie przy pierwszej
poprawce.

UWAGA: przy issue_comment GitHub uruchamia workflow z galezi DOMYSLNEJ.
Komenda zacznie wiec dzialac dopiero po zmergowaniu tego PR-a — na nim samym
akceptacja idzie jeszcze etykieta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Poprzednia wersja wymagala napisania /zrzuty ok w komentarzu. Zle zrozumialem
potrzebe: chodzi o mozliwosc OBEJRZENIA, nie o rytual zatwierdzania. Komenda
wypada, wraca sama etykieta.

RAPORT ZAMIAST ZIPA. .github/podglad-zrzutow.sh sklada jedna strone na PR:
nowe widoki i trojki wzorzec / teraz / roznica. Laduje na technicznej galezi
`podglad-zrzutow` jako README.md katalogu — GitHub renderuje to jako zwykla
strone, wiec wchodzi sie w odnosnik z komentarza i przewija obrazki. Bez
pobierania, bez rozpakowywania, bez komputera. Artefakt z raportem HTML
zostaje jako droga zapasowa.

SPRZATANIE PO TYGODNIU. Kazdy raport ma stempel czasu; przy kazdym przebiegu
znikaja te starsze niz 7 dni, razem z pustymi katalogami PR-ow. Po tygodniu
PR jest dawno zmergowany, a galaz nie ma rosnac w nieskonczonosc.

Zadanie `kontekst` zostaje — rozstrzyga w jednym miejscu numer PR-a, galaz
i to, czy jest etykieta. workflow_dispatch nie ma ani PR-a, ani etykiet, wiec
bez tego kazde zadanie radziloby sobie z tym osobno.

Tresc komentarza siedzi w .github/komentarz-zrzutow.js — ta sama obsluguje oba
zestawy, a skopiowana w dwoch miejscach rozjechalaby sie przy pierwszej
poprawce. Komentarz ma teraz jedno zadanie: dac odnosnik do raportu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Samo continue-on-error nie wystarczalo. Workflow konczyl sie zielono, ale przy
PR-ze i tak byl czerwony znaczek przy zadaniu — a to czyta sie jak zepsuty
build i kaze "naprawiac" cos, co bywa dokladnie zamierzone.

Teraz testy leca z `set +e`, a ich kod wyjscia jedzie dalej jako outputs.wynik
i konczy w komentarzu. Kroki od raportu i od dopisywania wzorcow maja `|| true`,
komentarz ma continue-on-error. Zaden z nich nie ma prawa niczego zatrzymac.

Bez zmian zostaja kroki instalacyjne i build — ich awaria to prawdziwa usterka,
nie roznica w wygladzie, i ma byc widoczna.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Przebieg na tym PR-ze mial 40 nowych widokow i zero zmienionych. Skrypt padl
po cichu, a komentarz powiedzial "raportu nie udalo sie wystawic".

Przyczyna: `set -e` z `pipefail` i `grep '^roznica__'`, ktory bez trafienia
zwraca 1 i ubija caly skrypt. Krok mial `|| true`, wiec zadanie bylo zielone
i nic tego nie zglosilo. Kazdy grep ma teraz wlasne `|| true`.

Drugi blad z tej samej zmiany: zadanie ze scenariuszami czytalo jeszcze
steps.test.outcome zamiast outputs.wynik. Po przejsciu na `set +e` outcome
jest ZAWSZE "success", wiec komentarz nie mial jak rozpoznac udanego przebiegu.

Trzecia rzecz, juz nie blad: brak obrazkow przy nieudanym przebiegu znaczy
co innego niz zmiana wygladu — test padl, zanim doszlo do porownania.
Komentarz mowi to teraz wprost, zamiast wysylac w strone "widoki sie zmienily".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants