Skip to content

Claude/kind ptolemy 9 mvh0 - #42

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

Claude/kind ptolemy 9 mvh0#42
fpudelko wants to merge 717 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
fpudelko and others added 28 commits August 6, 2026 17:29
feat: gość dopisany ręcznie przejmuje swój wpis po założeniu konta
…kart

Dzwonek pokazywal zero mimo trzech czekajacych zaproszen. Powiadomienia
(025) powstaly wczesniej niz imienne zaproszenia (060) i nikt tych dwoch
rzeczy wtedy nie polaczyl.

- migracja 067: wyzwalacz na event_player_invites tworzy powiadomienie
- uzupelnienie zaleglych zaproszen wstecz, ale tylko nieodrzuconych
  i dotyczacych meczow, ktore sie jeszcze nie odbyly
- cofniete obramowanie i naglowek ZAPROSZENIE na karcie - wygladalo ciezko
  przy kilku zaproszeniach pod rzad. Przycisk odrzucania zostaje.

UWAGA: migracja 067 wymaga recznego uruchomienia w Supabase SQL Editor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
fix: zaproszenie na mecz trafia do powiadomień; cofnięte obramowanie kart
Przycisk na cala szerokosc w odstepie od karty czytal sie jak osobny
wiersz listy - przy trzech zaproszeniach pod rzad nie bylo wiadomo,
ktorego dotyczy. Teraz waski, dosuniety do prawej krawedzi karty, bez
przerwy nad soba i z zaokragleniem tylko od dolu, wiec widac przynaleznosc.

Dzwonek powiadomien byl tylko w naglowku desktopowym (hidden md:flex).
Na telefonie nie bylo go nigdzie - powiadomienia istnialy, ale nie dalo
sie ich zobaczyc. Wstawiony obok hamburgera: dolna nawigacja ma wszystkie
piec miejsc zajetych, a naglowek to jedyny element obecny na kazdym
ekranie. Lista rozwijana dostala szerokosc ograniczona do okna.

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

ODbL wymaga uznania autorstwa wszedzie, gdzie pokazujemy dane z OSM, a nie
tylko pod mapa. Strona obiektu pokazuje nazwe, nawierzchnie, wymiary
i udogodnienia wprost z OSM - brakowalo tam atrybucji. Przy okazji link
'Zglos poprawke' prowadzacy do notatki OSM w tym punkcie.

Backlog: zglaszanie bledow (osobno aplikacja, osobno dane obiektu -
inny odbiorca i inny cykl zycia) oraz otwarte punkty zgodnosci z ODbL.
Przejecie profilu goscia oznaczone jako zrobione.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Dolny pasek nawigacji jest fixed i sam z siebie nie rezerwuje miejsca w
dokumencie. Robił to element-dystans w BottomNav.tsx, ale BottomNavGate
montuje się w layoucie PO {children}, więc dystans lądował poza kontenerem
strony — dokument dla zalogowanego na mobile miał zawsze 100vh + 64px
i każda strona przewijała się w pusty pas tła, nawet gdy treść mieściła
się na ekranie.

Nowe miejsce dla paska: zmienna --bottom-nav-h w globals.css, ustawiana
atrybutem na <html> przez BottomNavGate tylko wtedy, gdy pasek faktycznie
jest widoczny. Cztery miejsca z zaszytą na sztywno (i niezgodną ze sobą)
wysokością paska — CookieBanner, dwa miejsca w VenueExplorer — sprowadzone
do tej jednej zmiennej.
…zed meczem

Cztery zmiany w przepływie tworzenia i edycji meczu:

- Kreator zapamiętuje wypełniany formularz w localStorage na 12 godzin
  (lib/eventDraft.ts) i przywraca go po powrocie, z paskiem "Wróciliśmy
  do Twojego szkicu" + "Zacznij od nowa". Pomijane przy wejściu z
  ?group=/?fieldId= (kolizja z ich własnym prefillem).

- Numer do BLIKA: dokładnie 9 cyfr, formatowane 3-3-3 w trakcie pisania
  (formatBlikPhone). Na stronie meczu widzi go organizator zawsze,
  uczestnik ze składu dopiero godzinę przed startem (canSeeBlikPhone,
  minutesUntilStart) — z wyjątkiem okna "Dołączam", gdzie numer jest
  potrzebny od razu, żeby zapłacić przy zapisie. Publikacja/edycja meczu
  z niepełnym numerem albo zniżką karty wyższą niż koszt jest blokowana
  (validatePayments, wpięta w krok 2 kreatora i w edytuj/page.tsx).

- Kreator domyślnie wpisuje koszt za cały obiekt, nie od osoby — licząc
  cenę per uczestnik na bieżąco (efekt, nie tylko onChange), więc zmiana
  liczby miejsc po wpisaniu kwoty nie zostawia jej nieaktualnej.

- Tytuł meczu na stronie meczu i w oknie "Dołączam" pokazuje tę samą
  wartość co karty na liście (eventDisplayTitle) zamiast zbitki
  sport+rozmiar bez formatowania.

Bramka widoczności BLIKA działa wyłącznie w interfejsie — kolumna
blik_phone nadal przyjeżdża w całym wierszu events (zadanie w BACKLOG.md).
Placeholder tytułu w kreatorze ("np. Czwartkowa ligówka") nie mówił, jak
mecz się nazwie po zostawieniu pustego pola — a fallback był policzony
inaczej w pięciu miejscach (surowy event.sport, sportLabel(), własne
skróty EventListCard). lib/eventTitle.ts to jedno źródło prawdy
(defaultEventTitle/eventDisplayTitle); kreator pokazuje teraz realną
nazwę meczu w placeholderze i pod polem.
…zta na /profilu

Poniżej 768px zalogowany dostawał jednocześnie logo + hamburger w headerze
i te same skróty w dolnej nawigacji — a wszystko, co było w hamburgerze
(Moje mecze, Grupy, Moje obiekty, panel admina, motyw, Wyloguj), już
istniało gdzie indziej. Header dla zalogowanego na mobile to teraz
h-12 bez logo, z dzwonkiem powiadomień i awatarem prowadzącym do /profil
(skutek uboczny: dzwonek, wcześniej tylko na desktopie, jest teraz
dostępny na telefonie). Hamburger zostaje wyłącznie dla wylogowanych.

/profil dostał nowe sekcje: Moje obiekty (hasManagedVenue), Wygląd
(przełącznik motywu), Panel administratora (lib/adminLinks.ts — ta sama
lista, co w AdminMenu na desktopie). hasManagedVenue przeniesione z
zapytania Supabase wprost w Header.tsx do lib/api.ts.

Desktop bez zmian.
Usunięty nagłówek "Twoje mecze" i przycisk "+ Nowy mecz" (mecz tworzy się
z FAB-a w dolnej nawigacji, dostępnego z każdego ekranu). Zakładka
"Nadchodzące" renderuje te same komponenty co pulpit zalogowanego
(InvitesSection, NextMatchCard, MyMatchesSection, ObservingSection)
zamiast osobno utrzymywanej listy — bez sekcji "Twoje grupy"/"Otwarte
mecze", które mają własne strony.

DashboardSections.tsx dostał opcjonalne limit/href (null = pełna lista
bez obcięcia i bez linku "Wszystkie" wracającego na tę samą stronę) oraz
title/subtitle na ObservingSection i kontrolowane dismissedIds/onDismiss
na InvitesSection — wszystko z domyślnymi wartościami równymi
dotychczasowemu zachowaniu pulpitu, więc strona główna nie zmienia się
wizualnie ani o piksel.
…mobile

docs/domena.md: nowe moduły lib/ (eventDraft, eventTitle, adminLinks,
hasManagedVenue), reguła widoczności numeru BLIK w sekcji Płatności.
docs/funkcje.md: mechanizm --bottom-nav-h, górny pasek dla zalogowanych
na mobile + /profil, szkic kreatora, układ /moje-gry. BACKLOG.md: zadanie
o numerze BLIK nadal przyjeżdżającym w całym wierszu events (bramka tylko
w UI). docs/llm-context.md: dwa nowe wpisy w "Ostatnie zmiany" (limit 10 —
usunięte dwa najstarsze), zaktualizowana liczba testów w nagłówku;
frontend/public/llm-context.md zsynchronizowany (npm run sync:llm-context).
Pod karta byla tylko odmowa, wiec zapisanie sie wymagalo wejscia na strone
meczu: odpowiedz 'tak' kosztowala trzy klikniecia, 'nie' jedno. Teraz obie
sa na miejscu - zielona 'Dolaczam' zapisuje od razu, czerwona 'Odrzuc'
chowa zaproszenie.

Zielona jest szeroka i po prawej, czerwona waska po lewej: odmowa nie ma
wygladac na rownorzedna zachete.

Zapis idzie bez wyboru bramkarza i metody platnosci - te ustawienia zmienia
sie potem na stronie meczu. Blokowanie odpowiedzi 'tak' do czasu ich podania
odwracaloby proporcje.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Zdarzenia pull_request przestaly odpalac workflow - kilka PR-ow z rzedu
nie dostalo zadnego przebiegu, choc ten sam kod po merge'u przechodzil na
masterze. Push zadzialal za kazdym razem.

Bramka, ktora czasem milczy, jest gorsza od jej braku: brak przebiegu
wyglada w interfejsie podobnie do zieleni i kusi, zeby zmergowac.

Galezie claude/sql/** pominiete - niosa wylacznie zapytanie do bazy.

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

feat: dzwonek na mobile, zaproszenia z odpowiedzią „Dołączam", atrybucja ODbL
Po dodaniu dzwonka do naglowka mobilnego komponent renderuje sie dwa razy -
raz w wersji mobilnej, raz w desktopowej, jeden ukryty CSS-em, ale oba
zamontowane. Oba wolaly channel('notifs-<userId>'), a Supabase przy
powtorzonej nazwie oddaje istniejacy kanal zamiast tworzyc nowy. Drugi
komponent trafial na kanal juz zasubskrybowany i jego .on() konczylo sie
wyjatkiem 'cannot add postgres_changes callbacks after subscribe()'.

Nazwa kanalu unikalna per instancja przez useId(), z odsianiem dwukropkow,
ktore useId wstawia, a ktore trafilyby do topiku Phoenixa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
fix: dwa dzwonki tworzyły ten sam kanał realtime
…odztwie

Geofabrik wystawia poland-latest.osm.pbf, ale to ~1,7 GB, a osmium musi
przy czytaniu trzymac w pamieci indeks pozycji wezlow - na runnerze
GitHuba to gra o to, czy sie zmiesci. Do tego jeden blad w polowie
przewala caly przebieg.

Macierz 16 wojewodztw: miesci sie w pamieci, konczy szybciej (kilka
regionow naraz) i izoluje awarie - fail-fast wylaczony, wiec padniecie
jednego nie zatrzymuje pozostalych.

max-parallel domyslnie 4, bo ograniczenie jest po stronie Geofabrika:
szesnascie jednoczesnych pobran z jednego serwera to prosta droga do
odciecia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
feat: workflow importujący OSM dla całej Polski, województwo po województwie
Oba probowane uklady - obwodka z naglowkiem i para przyciskow Dolaczam
/Odrzuc - przeciazaly liste wizualnie. Karta zaproszenia wraca do samej
karty, odpowiada sie z poziomu meczu.

Przy okazji posprzatane martwe okablowanie: stan dismissedIds i handlery
w DashboardSections oraz moje-gry, ktore nie mialy juz kto wywolac.

Potrzeba zostaje w backlogu razem z powodami odrzucenia obu podejsc
i kierunkami na nastepne - problem jest prawdziwy, bo 'tak' kosztuje
wiecej klikniec niz 'nie'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
revert: zaproszenia bez przycisków odpowiedzi
Przy zlej nazwie regionu Geofabrik oddaje listing katalogu w HTML ze
statusem 200. Skrypt zapisywal go jako .pbf i wybuchal dopiero w osmium
komunikatem 'invalid BlobHeader size', z ktorego nie da sie odczytac, ze
chodzilo o literowke w nazwie.

- odrzucenie po content-type (html/text)
- sprawdzenie podpisu OSMHeader w pierwszych 64 bajtach pliku, bo
  rozszerzenie w adresie niczego nie gwarantuje
- to nie jest blad do ponawiania: zla nazwa nie stanie sie dobra za piatym
  pobraniem, wiec przerywa dane zrodlo od razu, jak przy 404
- plik czesciowy sprzatany, zeby nie zostal wziety za gotowy

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
fix: import odrzuca pobrany HTML zamiast dławić się nim w osmium
…tenera

Po zwinieciu paska adresu w Safari widoczna wysokosc okna rosnie. Kontener
mapy mial h-screen (100vh), ktory tego nie zauwaza, a dolna nawigacja jest
fixed, wiec trzyma sie widocznego okna. Te dwie rzeczy rozjezdzaly sie
o kilkadziesiat pikseli: podpowiedz wisiala w powietrzu daleko nad panelem,
a karta obiektu chowala sie pod nim.

- strona mapy uzywa 100dvh zamiast 100vh (dvh sledzi zwijanie paska)
- karta i podpowiedz sa fixed, tak jak nawigacja, wiec obie mierza od tej
  samej krawedzi
- z-index nad nawigacja, zeby przycisk zamkniecia nie wpadl pod pasek

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

fix: karta boiska i podpowiedź trzymają się dolnej nawigacji, nie kontenera
Cztery drobne moduly wyciagniete pod nadchodzace przeprojektowanie
/wydarzenia i /grupy/[id]. Zadnej zmiany widocznej dla uzytkownika.

- components/ui/FilterPill.tsx — PillDropdown i TogglePill przeniesione
  z VenueExplorer.tsx, gdzie byly prywatne. Portal do body zostaje: pasek
  filtrow bywa overflow-x-auto i przycinalby rozwiniete menu. Doszlo tylko
  aria-expanded i aria-pressed.
- lib/plural.ts — polska odmiana przez liczbe. W trzech miejscach stala
  regula `n < 5`, poprawna wylacznie dla 1-9: dla 12-14 dawala "mecze"
  zamiast "meczy". Regula mod-10/mod-100 z pelnym pokryciem testami.
- lib/searchText.ts — foldText/foldedIncludes. Szukanie na /wydarzenia
  robilo includes na surowym stringu, wiec "pilka" nie znajdowalo
  "pilka nozna". Ta sama technika co slugify(), ale ze spacjami.
- lib/geo.ts — distanceKm() przeniesione z prywatnego haversineM
  w app/admin/przeglad. Sortowanie "najblizej mnie" potrzebuje tego samego
  rachunku, a dwie kopie wzoru to dwie okazje do rozjazdu. Prog duplikatu
  w panelu admina przeliczony z 150 m na 0,15 km — bez zmiany zachowania.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdUx35rru2vLECFk3wR8TA
Podglad w hero pokazywal jeden ekran, i to niepelny. Przyczyna nie byla
przycieciem: PhoneMock nie mial zadanej ani wysokosci, ani proporcji, wiec
ramka brala wysokosc ze swojej tresci — a tresci byla jedna karta.

Teraz proporcje trzyma PhoneShell (aspect-[9/19]) i to ona wymusza pelny
ekran telefonu. Trzy makiety do przewiniecia palcem:

- Twoje mecze (/moje-gry) — waski pasek z dzwonkiem i awatarem, zakladki,
  Najblizszy mecz, Twoje najblizsze mecze, Obserwujesz, dolna nawigacja
- Tworzenie meczu (/wydarzenia/nowe, krok 2) — z kosztem obiektu jako
  trybem domyslnym i przeliczeniem na kwote od osoby
- Strona meczu (/wydarzenia/[id]) — sklad z lista graczy, koszt, komentarze

Makiety rysowane w JSX, nie zrzutami ekranu: zrzut mieknie powyzej 2x DPI
i rozjezdza sie z aplikacja przy pierwszej zmianie wygladu — pliki
w public/mockups/ maja dzis stare logo i widoczne szkielety ladowania
zamiast tresci. JSX idzie za tokenami kolorow sam.

Karuzela na natywnym scroll-snapie, bez biblioteki. Aktywny slajd czytany
z pozycji przewiniecia, wiec gest palcem i klik w kropke nie moga sie
rozjechac. Auto-przewijanie konczy sie na zawsze przy pierwszym dotknieciu
i nie startuje przy prefers-reduced-motion.

Zniknelo md:rotate-[3deg] — obrocony kontener przewijany w bok to mylace
trafienia palcem i przekrzywione kropki.

Zweryfikowane w przegladarce: gest aktualizuje kropki, wszystkie trzy
slajdy maja 564 px wysokosci (brak skakania w pionie), strona nie jest
rozpychana w poziomie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdUx35rru2vLECFk3wR8TA
Na telefonie wylogowany widzial "Zaloguj sie" + hamburger. Teraz, od lewej:
ikona mapy (/mapa), zielony przycisk "Dolacz" i ikona awatara (logowanie).

"Dolacz" prowadzi na /logowanie?mode=rejestracja i otwiera formularz od razu
na zakladaniu konta — bez tego przycisk obiecywalby rejestracje, a pokazywal
logowanie. AuthForm dostal opcjonalny prop initialMode; domyslnie nadal
'signin', wiec pozostale 20 wejsc na /logowanie zachowuje sie bez zmian.

Hamburger znika calkowicie, razem z arkuszem menu (91 linii), pulapka focusa,
blokada scrolla i stanem mobileOpen. Konsekwencja swiadoma: pasek przestaje
byc nawigacja dla wylogowanego. Do /wydarzenia i /wydarzenia/nowe prowadza
CTA w hero, kafelek w "Co dostajesz", plywajacy przycisk + oraz stopka.

Desktop dostaje oba wejscia z nazwami — tekstowe "Zaloguj sie" i zielone
"Dolacz" — bo tam jest na to miejsce i chowanie logowania pod ikone nic
by nie dalo.

Przy okazji: usuniete martwe importy (Menu, X, ChevronRight, Search) oraz
TeamIcon — komponent zdefiniowany, nigdy nierenderowany, martwy juz przed
ta zmiana.

Zweryfikowane w przegladarce: linki prowadza pod /mapa,
/logowanie?mode=rejestracja i /logowanie, hamburgera nie ma,
a ?mode=rejestracja otwiera naglowek "Zaloz konto" zamiast "Zaloguj sie".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdUx35rru2vLECFk3wR8TA
Landing mial cztery identyczne przyciski "Zorganizuj mecz" — hero,
"Jak to dziala", "Co dostajesz" i pas pod FAQ. Cztery slabe wezwania
zamiast jednego mocnego.

Zostaja dwa: hero i kafelek w "Co dostajesz".

"Jak to dziala" traci przycisk spod listy, a zyskuje klikalny pierwszy krok
"Stworz mecz" — sekcja prowadzi do akcji wlasna trescia, zamiast powtarzac
pod spodem to, co stoi juz w hero. Krok z `href` (dzis tylko pierwszy)
renderuje sie jako Link ze strzalka; pozostale zostaja opisem.

Pas pod FAQ znika w calosci, nie sam przycisk: "Twoja ekipa czeka na termin"
istnialo wylacznie po to, zeby ten przycisk hostowac — bez niego zostalaby
deklaracja bez akcji tuz nad stopka. Razem z sekcja znika stala
LANDING_FINAL_CTA i jej odwolania w tescie.

Uwaga: SiteFooter ma wlasny, tekstowy link "Zorganizuj mecz". To zwykla
pozycja nawigacji w stopce, nie przycisk — nie byl w liczonej czworce
i zostaje.

Nowy test pilnuje, ze pierwszy krok ma href do /wydarzenia/nowe, a pozostale
go nie maja — bez tego usuniecie href zostawiloby sekcje bez drogi dalej.

Zweryfikowane w przegladarce: na landingu widoczne sa dokladnie dwa przyciski
CTA (hero + "Co dostajesz") plus link w stopce, po FAQ nastepuje od razu
stopka, a klikniecie kroku 1 prowadzi na /wydarzenia/nowe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdUx35rru2vLECFk3wR8TA
claude and others added 29 commits August 16, 2026 17:11
…alna

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
…aude/zacheta-instalacji

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
Bojo jako apka na ekranie głównym (PWA, etap 1)
Zachęta do dodania Bojo na ekran główny (PWA, etap 1b)
Backlog: katalog zrobiony, nowa przesłanka strategiczna, plan PWA
…kuje graczy

Zakładka "Ustawienia" na stronie meczu znika teraz razem z treścią (przycisk nie był gated na canManageEvent). Ikona "Grupy" na dolnej nawigacji nosi dwie kropki: różową z lewej (nieprzeczytane wiadomości), pomarańczową z prawej (nowy mecz w ekipie). Filtr "tylko z nieprzeczytanymi" na /moje-gry przeniesiony na wysokość "Brakuje graczy". Zaktualizowano zrzut kreatora w karuzeli landingu.
…ny-obszar

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
Zgloszone ze zrzutu z zainstalowanej apki: dolny pasek siedzi za nisko,
a podpis "Nowy" przecina kreska gestow iPhone'a.

PRZYCZYNA: brak `viewport-fit=cover`. Bez niego `env(safe-area-inset-*)`
zwraca ZERO — a repo liczy na te wartosci w kilku miejscach: BottomNav ma
`paddingBottom: env(safe-area-inset-bottom)`, `--bottom-nav-h` dodaje ten sam
inset, karty na mapie sie od niego odsuwaja. Wszystkie te wywolania byly
martwe. W karcie przegladarki nie bylo tego widac, bo miejsce na pasek gestow
rezerwowal sam Safari; po instalacji jako apka nie robi tego nikt.

Przy okazji dwa miejsca, ktore odsuwaly sie WYLACZNIE o wysokosc nawigacji:
- baner cookies i zacheta do instalacji uzywaly `var(--bottom-nav-h)`, ktore
  wynosi 0, gdy paska nie ma (wylogowany, schowany pasek) — wtedy lezaly tuz
  nad kreska gestow. Nowa zmienna `--dol-bezpieczny` bierze `max` z wysokosci
  paska i samego insetu. Max, nie suma: `--bottom-nav-h` juz zawiera inset,
  wiec dodanie go drugi raz zostawialoby zbedna dziure.
- pasek "Dolacz" na stronie meczu CHOWA nawigacje (HideBottomNav), wiec
  `--bottom-nav-h` jest tam zerem, a samo `pb-6` konczylo sie przyciskiem tuz
  nad kreska.

Ta galaz niesie tez tresc PR-a #190 (zacheta do instalacji), ktora przez zla
kolejnosc merge'owania wyladowala na galezi #188 zamiast w masterze.

UWAGA: szesc zrzutow tras (strona glowna, dlaczego-bojo, jak-dziala-bojo, faq,
grupy, cykliczne) rozni sie od wzorcow — sprawdzone na CZYSTYM masterze
w osobnym worktree: pada tak samo bez tej zmiany. To zalegle wzorce po ostatnich
rundach UI, nie skutek tej poprawki.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
…e", dymki wyjaśniające

EventBrowseCard w ogóle nie renderowała plakietki nieprzeczytanych dla rozegranych meczów - naprawione. Filtr nieprzeczytanych na /moje-gry próbuje trzech miejsc po kolei zamiast zajmować pusty wiersz na sztywno. Nazwa ekipy w belce /grupy/[id] rozwija listę pozostałych ekip. Nowa sekcja "Twoja ekipa gra wkrótce" na pulpicie. Pomarańczowa kropka na konkretnym meczu (nie tylko zbiorczo) na /wydarzenia i w zakładce Mecze ekipy. Krótkie dymki tłumaczące znaczenie kropek na dolnej nawigacji, max 5 razy na typ.
…kundy (#194)

Cztery zapytania w BottomNav mogły nadpisać świeży stan przestarzałą
odpowiedzią z poprzedniej trasy - teraz każde ignoruje wynik, jeśli
komponent zdążył się przełączyć w trakcie oczekiwania. Dymki wyjaśniające
kropki pokazują się teraz pojedynczo z globalnej kolejki (zamiast
niezależnie per ikona) i trzymają się 4 sekundy zamiast 1.5.


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

Co-authored-by: Claude <noreply@anthropic.com>
…nie wystaje poza ekran (#195)

Poprzednia naprawa wyścigu nie wystarczyła - każdy .catch(() => {}) w
BottomNav.tsx zostawiał poprzednią wartość stanu przy błędzie zapytania,
więc jedno nieudane zapytanie po udanym "są nieprzeczytane" trzymało
różową kropkę zapaloną bez związku z rzeczywistością. catch ustawia
teraz jawnie false/null. Dymek nad pierwszą i ostatnią z pięciu ikon
wyśrodkowywał się blisko krawędzi ekranu i wystawał poza nią - skrajne
kolumny przypinają go teraz do swojej wewnętrznej krawędzi.


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

Co-authored-by: Claude <noreply@anthropic.com>
…bazy

Trzy zgloszenia z telefonu, dwa maja wspolna przyczyne.

1. PRZYPISYWANIE DO DRUZYN "NIC NIE ROBILO". Ani przesuniecie gracza w lewo
   i prawo, ani przyciski N i C. `updateParticipantTeam` robilo gole
   `.update()` — a przy niepasujacej polityce RLS Postgres zmienia ZERO
   wierszy i zwraca SUKCES. Zadnego bledu, zadnego komunikatu, `load()`
   przerysowuje ten sam stan. Dokladnie pulapka opisana w AGENTS.md, dla
   ktorej istnieje `zaktualizujJedenWiersz()` — teraz uzyta tu i przy
   zapisie platnosci.

2. PRZELACZNIK ADMIN/UZYTKOWNIK przelaczal sie na ekranie i wracal po
   odswiezeniu. Ta sama cisza, ale z prawdziwa odmowa pod spodem: polityka
   z migracji 022 sprawdzala uprawnienie podzapytaniem o `profiles`
   WEWNATRZ polityki na `profiles`. Podzapytanie samo podlega RLS tej tabeli,
   wiec warunek wychodzil falsz i UPDATE nie ruszal nic. Migracja 098 wynosi
   sprawdzenie do `czy_admin()` (SECURITY DEFINER, STABLE, ustawiony
   search_path) i przepina na nia takze polityke z 005.

3. ZAKLADKI. Podzial na druzyny mieszkal w zakladce "Wynik", wiec zeby
   poukladac sklady PRZED meczem trzeba bylo wejsc w wynik, ktorego jeszcze
   nie ma. Teraz "Sklady" to wlasna zakladka obok "Skladu" (lista zapisanych),
   a "Wynik" pojawia sie dopiero po rozpoczeciu meczu i zawiera sam formularz.
   Wczesniej zakladka Wynik istniala od utworzenia meczu i po klknieciu
   mowila tylko, ze wyniku jeszcze nie ma — zajmowala miejsce w pasku, nie
   dajac nic w zamian. Zwijana kopia druzyn w zakladce Sklad zamieniona na
   odnosnik, zeby nie bylo dwoch wejsc do tego samego.

MIGRACJA 098 DO URUCHOMIENIA RECZNIE w Supabase — bez niej punkt 2 zostaje
zepsuty, tyle ze teraz z widocznym komunikatem zamiast ciszy.

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

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
Poprawka wlasnej poprzedniej wersji, po obejrzeniu jej przez wlasciciela.

DRUZYNY WRACAJA DO SKLADU. Probowalem dwoch gorszych ukladow: najpierw zwijana
sekcja w Skladzie (chowala rzecz, po ktora sie tam wchodzi), potem osobna
zakladka "Sklady" (rozdzielala sklad od jego podzialu). Teraz podzial jest
widoczny WPROST w zakladce Sklad, bez klikania.

ROZLICZENIA znikaja z paska, gdy mecz jest za darmo. `platnosciSection`
renderuje sie wylacznie przy `costGrosze > 0`, wiec przy darmowym meczu
zakladka otwierala sie PUSTA — sam naglowek i nic pod spodem. Zgloszone
wprost ze zrzutu.

WYNIK bez zmian wobec poprzedniej wersji: pojawia sie dopiero po rozpoczeciu
meczu i zawiera sam formularz.

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

# Conflicts:
#	docs/llm-context.md
#	frontend/public/llm-context.md
Pasek nawigacji pod kreską gestów iOS + zachęta do instalacji (dogranie #190)
Awaria u uzytkownika nie zostawiala ZADNEGO sladu. app/error.tsx wypisywal
blad do konsoli przegladarki, ktorej nikt nie oglada, a zgloszenie "cos mi
wywalilo" przychodzilo zrzutem ekranu bez adresu strony, wersji i tresci bledu
— czyli w formie drozszej do odtworzenia niz sama naprawa.

DLA UZYTKOWNIKA: /zglos-blad, jedno pole. Kazde kolejne (kategoria,
powtarzalnosc, priorytet) odsiewa zglaszajacych i tak czy siak wypelnia sie je
byle jak, a to, co naprawde potrzebne, doklejamy sami: adres, przegladarka,
wersja, id uzytkownika. Dziala bez logowania — awaria na stronie meczu
otwartej z linku jest dokladnie tym przypadkiem, o ktorym chcemy wiedziec.
Odnosnik w stopce, czyli na kazdej stronie: awaria rzadko zdarza sie tam,
gdzie akurat wisi przycisk.

AUTOMATYCZNIE: window.onerror i unhandledrejection (ten drugi to najczestszy
sposob, w jaki psuje sie apka rozmawiajaca z baza — await bez catch, zapytanie
pada, ekran zostaje pusty). Plus samo app/error.tsx, razem z `digest`, ktorym
Next laczy blad z logiem serwerowym.

ZEBY TO NIE BYL SMIETNIK, cztery rzeczy:
- GRUPOWANIE po odcisku: komunikat + pierwsza ramka stosu, z wycietym hashem
  builda i numerami linii. Bez tego jeden zepsuty widok daje setki kopii,
  a licznik wystapien — najwazniejsza liczba przy awarii, bo mowi czy dotyczy
  jednej osoby czy wszystkich — w ogole nie istnieje. Wyciecie hashy sprawia,
  ze ten sam blad nie zaklada nowego wiersza po kazdym wdrozeniu.
- JEDEN BLAD NA SESJE plus twardy limit 10. Petla renderowania w Reakcie
  potrafi rzucic ten sam blad kilkadziesiat razy na sekunde.
- ZGLOSZENIE NIGDY NIE WYWRACA APLIKACJI — awaria mechanizmu raportowania
  awarii nie moze byc kolejna awaria widoczna dla uzytkownika.
- CZYTA WYLACZNIE ADMIN. To nie ostroznosc na wyrost: w kolumnie `adres`
  laduje link, ktory bywa linkiem do prywatnego meczu.

Zapis idzie wylacznie przez RPC (SECURITY DEFINER) — tabela nie ma polityki
INSERT, wiec klient nie decyduje o statusie, liczniku ani user_id; tozsamosc
bierzemy z auth.uid(), nie z tego, co przyszlo z przegladarki.

PANEL /admin/bledy: lista po ostatnim wystapieniu (blad sprzed tygodnia,
ktory wciaz sie dzieje, jest pilniejszy od wczorajszego, ktory ucichl), filtry
po statusie, stos w szczegolach, trzy stany. Zamkniety blad, ktory wraca,
sam wraca na "nowe".

MIGRACJA 099 DO URUCHOMIENIA RECZNIE. Wymaga 098 (czy_admin).

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

STOPKA BYLA ZLYM MIEJSCEM. SiteFooter jest importowany tylko w app/page.tsx
i w StronaTresci — czyli renderuje sie na landingu i stronach tresci, a NIE na
ekranach, na ktorych cos realnie pada: strona meczu, moje gry, mapa, ekipy.
Odnosnik w stopce zostaje (nic nie kosztuje), ale glowne wejscie jest teraz
w profilu, do ktorego zalogowany trafia z kazdego ekranu — tam, gdzie repo
trzyma juz panel admina, i z tego samego powodu.

BLAD W DANYCH OBIEKTU jako trzeci rodzaj zgloszenia. Osobny od bledu aplikacji,
bo ma innego odbiorce i inny cykl zycia: katalog pochodzi z OpenStreetMap
i NIE jestesmy jego wlascicielem.

Stad dwie drogi na stronie obiektu i obie maja sens:
- „Zglos poprawke" (bylo juz wczesniej) — notatka w OSM, naprawia dane
  U ZRODLA, z pozytkiem dla calego OSM,
- „Zglos blad w danych" (nowe) — trafia DO NAS, bo tylko my mozemy ukryc
  obiekt, ktorego w rzeczywistosci nie ma.

Lista zamknieta pieciu powodow zamiast pola tekstowego: pole daje „nie wiem",
a te piec pokrywa to, co ludzie realnie zglaszaja. Zgloszenie SWIADOMIE nie
zmienia zadnych danych — jedno kliknieciezlosliwej osoby nie moze wywrocic
katalogu. Progi, kolumny `override_*` i odsylanie poprawek do OSM zostaja
otwarte w backlogu, tak jak byly.

Migracja 099 rozszerzona (nie byla jeszcze uruchomiona): rodzaj `obiekt`,
kolumna `field_id` z ON DELETE CASCADE i indeks po niej — backlog zaklada,
ze dopiero kilka niezaleznych zgloszen uzasadnia zmiane bez moderacji, wiec
liczenie ich per obiekt musi byc tanie.

W panelu admina zgloszenie o obiekcie ma wlasna ikone i odnosnik „Otwórz
obiekt": przy takim zgloszeniu liczy sie jedno — dojsc tam jednym klknieciem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Zgłaszanie błędów: formularz dla ludzi i automatyczny log awarii
…anie sie

Nad licznikiem miejsc — najwazniejsza informacja na tej stronie — stalo pol
ekranu rzeczy drugorzednych.

GORNE „UDOSTEPNIJ / KOPIUJ" USUNIETE. Ta sama para stoi nizej, w karcie
„Wyslij link znajomym", gdzie ma naglowek i zdanie tlumaczace, po co to
klikac. Dwa wejscia do tej samej akcji na jednym ekranie kosztowaly miejsce
nad trescia, po ktora sie tu wchodzi.

DWA WIERSZE ZAMIAST CZTERECH. Zasada: pigulka jest elementem dla ETYKIETY —
krotkiej, powtarzalnej, w rodzaju „Za darmo". Data i nazwa boiska to nie
etykiety, tylko tresc o zmiennej dlugosci, a kazda tracila kilkadziesiat
pikseli na sama ramke i dopelnienie. Wiec:

- wiersz 1 (pigulki): status w meczu, cena, widocznosc, wymaga akceptacji,
  stala gierka, grupa — same krotkie etykiety,
- wiersz 2 (zwykly tekst z ikonami): data i godzina, czas trwania doklejony
  do godziny (bo to ta sama informacja, nie dwa fakty), miejsce.

Nazwa boiska ma dzieki temu dosc szerokosci, zeby nie urywac sie po trzech
slowach.

WYPISZ SIE — CZERWONE OD RAZU. Przycisk byl szary i czerwienial dopiero na
hover, czyli na telefonie NIGDY. Teraz czerwona ramka i czerwony tekst na
bialym tle. Swiadomie nie pelna czerwien: wypisanie sie jest odwracalne,
a pelna czerwien zostaje dla akcji nieodwracalnych typu „Usun na stale".

Przy okazji „Zapros z grupy" wychodzi wyzej samo — nad licznikiem ubylo
okolo polowy dotychczasowej wysokosci.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
fix: strona meczu — mniej pigulek nad licznikiem, czytelniejsze wypis…
…ekt→grupa

DWIE RZECZY, obie z dzisiejszego zgloszenia.

1. ROZMOWA — CZEGO JESZCZE NIE WIEM, POWIEDZIANE WPROST.
Blad "new row violates row-level security policy for table event_comments"
przy usuwaniu WLASNEJ wiadomosci jest wobec polityk z repo LOGICZNIE
NIEMOZLIWY. Migracja 026 daje:

    USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id)

Komunikat "new row violates" pochodzi z WITH CHECK, a ten odpala sie dopiero,
gdy USING przepuscil wiersz. Skoro USING przepuscil, to auth.uid() = user_id
bylo prawda — i ten sam warunek nie ma jak byc falszem sekunde pozniej na tym
samym wierszu, ktorego user_id nie ruszamy.

Wniosek: polityki na produkcji SA INNE niz w repo. To dokladnie ta rozbieznosc,
o ktorej ostrzega docs/baza-danych.md ("historia migracji w repo != historia
w Supabase"). Nie zgaduje wiec nowej polityki — do zgloszenia dolaczam
zapytanie diagnostyczne, ktore wypisze prawdziwy stan.

Co robie tym commitem, bo jest sluszne niezaleznie od przyczyny:
- deleteComment idzie przez zaktualizujJedenWiersz(). Kasowanie jest MIEKKIE
  (ustawia deleted_at), wiec niepasujaca polityka dalaby zero zmienionych
  wierszy i sukces: wiadomosc znikalaby z ekranu i wracala po odswiezeniu.
- nowy helper zPonowieniemPoOdswiezeniu(): zapis, ktory padl na wygaslej
  sesji, jest ponawiany raz po odswiezeniu tokenu. To osobna, realna pulapka
  na ekranach, gdzie karta wisi otwarta godzinami — token dostepu zyje godzine,
  aplikacja dalej wyglada na zalogowana, a baza odsyla komunikat o polityce
  bezpieczenstwa, z ktora piszacy nie ma nic wspolnego. Druga proba jest
  ostatnia, zeby nie zapetlic sie na czyms, czego odswiezanie nie naprawia.
  Bledy NIEZWIAZANE z sesja leca dalej nietkniete.
- podpiete tez pod tablice ekipy (groupPosts) — ten sam wzorzec uzycia.

2. SKRYPT obiekt→grupa (supabase/zadania/obiekt-do-grupy.sql).
Przypisuje wszystkie mecze z jednego boiska do grupy i dopisuje do niej
kazdego, kto zagral tam min. 2 razy. DWUCZESCIOWY: czesc A to same SELECT-y
i pokazuje, co sie stanie (w tym mecze, ktore zostalyby PRZEPIETE z innej
grupy); czesc B jest zakomentowana i wykonuje sie w jednej transakcji.

"Zagral" liczy waska definicja i jest to swiadome: bez gosci bez konta, bez
rezerwowych, bez nieakceptowanych prosb. Identyfikatory wpisane wprost —
edytor SQL w Supabase to nie psql i nie zna \set ani :nazwa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
Rozmowa: głośne błędy i ponowienie po wygasłej sesji + skrypt obiekt→grupa
Na liście składu ekipy każdy członek miał plakietkę Współorganizator.
Powód: warunek w SkladGrupy sprawdzał cztery flagi na OR, a dwie z nich
(can_create_events, can_invite) mają w bazie DEFAULT true — to prawa
zwykłego członka, istniejące po to, żeby dało się je komuś odebrać.
Wyróżnienie, które dostaje każdy, nie wyróżnia nikogo.

Odznakę liczy teraz czyWspolorganizator() z lib/groups.ts na tych samych
dwóch uprawnieniach, z których trigger ustaw_role_czlonka (migracja 092)
wylicza role = 'admin': zarządzanie składem i moderowanie tablicy.
Etykieta w UI i rola w bazie przestają się rozjeżdżać.

Nadawanie uprawnień bez zmian: panel widzi wyłącznie założyciel
(polityka UPDATE na group_members z 092).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dvpk3tbb8nsot5XNTaS8C
@fpudelko
fpudelko force-pushed the claude/kind-ptolemy-9Mvh0 branch from 45726f0 to 5873e54 Compare August 17, 2026 20:34
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