Rejestracja (/logowanie):
- Formularz rejestracji (osoba prywatna i firma) zawsze zaczyna sie pusty.
Logowanie i rejestracja dzielily pola e-mail i hasla, wiec dane wpisane
albo podstawione przez przegladarke przy logowaniu trafialy do nowego konta.
- "Powtorz haslo" ma wlasny przelacznik widocznosci; wczesniej oko przy
"Haslo" odslanialo oba pola, a ikona przy "Powtorz haslo" nie reagowala.
- Przycisk oka ma normalny obszar klikniecia (mial zerowy rozmiar).
Kod SMS:
- Okno potwierdzania numeru odlicza czas od SMS-a z rejestracji, a odmowa
serwera ("Kolejny mozesz zamowic za N s") odlicza czas w dol zamiast
pokazywac stala liczbe sekund. To samo w ustawieniach konta.
Panel admina:
- "Wysylka kampanii e-mail aktywna" -> "Wysylka e-maili aktywna" z opisem:
przelacznik blokuje wszystkie e-maile (aktywacja, reset hasla,
powiadomienia), nie tylko kampanie. Podobny opis przy SMS.
Wiadomosci:
- Puste pole pisania bez "Napisz wiadomosc do ...".
- Naglowek rozmowy "Nazwa - opis" zamiast sklejonego tekstu.
- Na liscie rozmow jedna miniatura ogloszenia zamiast dwoch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Obok "Ulubione" i dzwonka powiadomien - dla kazdego zalogowanego konta,
takze administratora - ikona prowadzaca do /wiadomosci z plakietka liczby
nieprzeczytanych, na wzor Gieldy Komunalnej.
Licznik nieprzeczytanych wydzielony do useMessagesUnreadCount, wspolnego
dla menu konta i paska gornego; odswieza sie co 25 s i po zmianie strony.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dotad rozmowy z kupujacymi istnialy tylko w przegladarce nadawcy: nic nie
trafialo na serwer, "odczytano" i odpowiedz sprzedajacego byly symulowane
timerem. Wzorem Gieldy Komunalnej:
Backend (/api/conversations):
- Conversation: jeden watek na pare (ogloszenie, kupujacy), sprzedajacy to
wlasciciel ogloszenia; tytul ogloszenia zapisany na wypadek jego usuniecia.
Wiadomosci w istniejacej tabeli messages z nowa kolumna conversation_id,
wiec rozmowa z administracja (conversation_id = null) dziala bez zmian.
- start/list/get/reply, blokada (UserBlock, w obu kierunkach), ukrycie
rozmowy u siebie (nowa wiadomosc przywraca ja obu stronom). Dostep tylko
dla stron rozmowy, filtr niedozwolonych slow jak w reszcie serwisu.
- Po zatwierdzeniu transakcji odbiorca dostaje powiadomienie w serwisie
i - jesli ma wlaczone powiadomienia o wiadomosciach - e-mail (w tle).
- /api/messages/unread-count wlicza rozmowy o ogloszeniach.
- Zgloszenie uzytkownika z rozmowy: zgloszonego, ogloszenie i tresc rozmowy
serwer bierze z bazy, nie z przegladarki.
Frontend:
- Adresy /wiadomosci/:conversationId, /wiadomosci/administracja
i /wiadomosci/nowa/:listingId zamiast rozmowy w location.state.
- Lista rozmow odswiezana co 30 s, otwarta rozmowa co 4 s.
- Zdjecia w wiadomosciach usuniete - serwer ich nie przyjmuje (rozmowa
z administracja i tak je gubila).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Strony pokazywaly zmyslone dane jako prawdziwe. Usuwam je i zostawiam
puste stany tam, gdzie funkcja nie ma jeszcze zrodla danych:
- strona glowna: 4 fikcyjne "Polecane ogloszenia" (pokazywane przy mniej
niz 4 ogloszeniach) - teraz prawdziwe ogloszenia albo pusty stan;
- mapa: zapasowe fikcyjne oferty kupna i wynajmu;
- wyszukiwarka: gotowe wpisy "Ostatnio wyszukiwane";
- negocjacje: zmyslona "szansa na zakup" i "podobne transakcje" - pusty stan;
- historia cen: wykres i statystyki z generatora - puste do czasu podpiecia API;
- alerty cenowe: wykres z generatora; srednia liczona tylko z ogloszen.
Wczesniej przy braku ogloszen podstawiana byla stawka szacunkowa, przez co
monitoring w tle zglaszal nieistniejace zmiany cen;
- spotkania: przykladowe i "testowe" spotkania dopisywane kazdemu kontu,
takze odfiltrowane z localStorage;
- wiadomosci: 7 fikcyjnych rozmow; pusty stan zamiast bledu bez watkow;
- moje oferty: 12 ukrytych fikcyjnych ofert zapisywanych w localStorage;
- poradniki: zmyslone polecane artykuly (teraz najczesciej czytane z API)
i liczniki "5 120 ofert";
- wycena: wskaznik "% dokladnosci" liczony z liczby wypelnionych pol;
- martwy kod z fikcyjnymi powiadomieniami, znacznikami mapy i statystykami.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scripts/cleanup-test-data.sql zostawia wylacznie konta zatorskipascal@gmail.com
i admin@softspm.com. Usuwa pozostale konta, ogloszenia spoza tych dwoch kont
oraz powiazane dane (wiadomosci, powiadomienia, ustawienia, leady, zamowienia
promocji, zgloszenia, poradniki), w tym sieroty po kontach usunietych wczesniej
z panelu admina. Domyslnie dziala na sucho (ROLLBACK), zapis tylko z -v apply=1;
przerywa, gdy ktoregos z zachowywanych kont nie ma w bazie.
DataSeeder nie dodaje juz ogloszen demo: robil to przy kazdym starcie z pusta
tabela ogloszen, wiec po wyczyszczeniu danych wracalyby przy kolejnym wdrozeniu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Endpoint /api/auth/social byl atrapa bez prawdziwego OAuth: wydawal token
sesji dla dowolnego adresu e-mail podanego w zadaniu, bez hasla. Podanie
adresu administratora dawalo pelna sesje z rola ADMIN.
Na tym etapie logowanie ma byc wylacznie dla uzytkownikow systemu (e-mail
i haslo), wiec usuwam je w calosci:
- backend: endpoint, wpis permitAll w SecurityConfig, AuthService.socialLogin
z pomocnikami i DTO SocialLoginRequest. Enum AuthProvider zostaje, bo
istniejace konta moga miec GOOGLE/FACEBOOK.
- frontend: przyciski Google/Facebook w formularzach logowania i rejestracji,
socialLogin w AuthContext oraz osierocone style.
Test regresyjny AuthControllerTest nie uruchomi sie jeszcze na H2 - start
kontekstu wywraca sie na split_part w SQL naprawiajacym nicki (ten sam blad
dotyczy istniejacego contextLoads). Zweryfikowane recznie na stacku
z PostgreSQL: /api/auth/social -> 403, logowanie e-mailem -> 200.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Trzy rzeczy kosztowaly dzis najwiecej czasu i zadnej nie dalo sie wyczytac z repozytorium:
zmienne z pliku srodowiskowego nie docieraja do kontenera, jesli nie ma ich w sekcji
environment; wszystkie aplikacje na serwerze leza w katalogach "app", wiec Compose bez -p
wchodzi w cudzy stack; loginem jest e-mail, a nie nick.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Logowanie z https://polskalokalnie.pl konczylo sie bledem 403, a nie 401 -
zadanie w ogole nie docieralo do logiki uwierzytelniania. Nginx terminuje TLS
i przekazuje ruch do backendu po http, wiec Spring widzi schemat http, podczas
gdy przegladarka przysyla Origin z https. Schematy sie roznia, wiec zadanie
jest traktowane jako cross-origin i sprawdzane wzgledem listy dozwolonych
originow, na ktorej byly wylacznie adresy lokalne i LAN. Frontend pokazywal
przy tym generyczny komunikat, bo odpowiedz CORS nie jest JSON-em z pole
"message".
Dodanie domeny produkcyjnej do listy przywraca poprawne zachowanie: zle haslo
zwraca 401 z czytelnym komunikatem, poprawne 200.
Poza tym:
- name: polskalokalnie w obu plikach compose. Bez tego Compose wyprowadza nazwe
projektu z nazwy katalogu, a na serwerze kazda z trzech aplikacji siedzi w
katalogu "app" - komenda bez -p wchodzila w stack innej aplikacji i podmieniala
w nim serwis backend.
- restart: unless-stopped na serwisach produkcyjnych. Kontenery polskalokalnie
mialy polityke "no" i jako jedyne na serwerze nie wstawaly po reboocie.
W pliku deweloperskim swiadomie tego nie ma - lokalnie kontenery nie powinny
startowac same przy kazdym uruchomieniu Dockera.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nazwa i adres:
- marka "Mieszko" -> "Polska Lokalnie" w calym interfejsie (27 miejsc),
w tekstach zaleznych dodane slowo "serwis", zeby brzmialy poprawnie po polsku
- domena mieszko.pl -> polskalokalnie.pl (adresy kontaktowe, profil, udostepnianie)
- haslo pod logo: "mieszkania za darmo" -> "ogloszenia za darmo"
- backend: teksty powiadomien, dane demo i domyslna nazwa admina
- nazwa wyswietlana istniejacego admina zaktualizowana w bazie;
login admin@mieszko.pl celowo bez zmian
Naglowek:
- logo, nawigacja i panel zostaja widoczne przy przewijaniu (position: sticky,
warstwa 65 - nad listami rozwijanymi, pod lightboxem zdjec)
- sticky sidebary z top: 18-20px wyrownane do 96px, zeby nie wjezdzaly pod naglowek
Uklad stron konta:
- /ulubione, /wiadomosci i /powiadomienia otwieraja sie jak /konto/wyszukiwania -
wspolny layout AccountShell zamiast powielania ukladu w kazdej stronie
- /konto/porownanie mialo wlasny uklad (sidebar 300px, inny gap i padding),
przez co menu przeskakiwalo - wyrownane do .account-layout wraz z punktem
zwijania (1200px zamiast 1500px)
- narzedzia publiczne (/wycena, /historia-cen, /ranking-dzielnic, /negocjacje)
bez menu konta - dzialaja jak /kupno czy /wynajem
Naprawy regresji po zamianie przyciskow na linki:
- ikona ulubionych na kartach /kupno wracala do przeplywu (nadpisany
position: absolute) - teraz podnoszona jest tylko warstwa
- menu uzytkownika i "Zobacz wszystkie" w powiadomieniach: reguly CSS
celowaly tylko w <button>, przez co pozycje rozjezdzaly sie na dwie linie
Testy: 47 (bylo 41), w tym zabezpieczenie przed powrotem przeskoku menu
i przed rozjechaniem pozycji menu uzytkownika.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nowa trasa:
- /firmy - lista firm i uslug na istniejacych komponentach (.directory-card)
i stylistyce sekcji uslug; filtr kategorii w query stringu (/firmy?kategoria=remonty)
- serviceCategories dostaly slug, nowa baza companyDirectory pokrywa wszystkie
8 kategorii, wiec filtrowanie dziala na realnych danych
- "Zobacz firmy" prowadzi do listy z wybrana kategoria, "Zobacz wszystkie"
do pelnej listy - ostatnie dwie atrapy href="#" usuniete
CTA zamienione na <Link> (wylacznie czysta nawigacja, bez akcji):
- 35 przyciskow onClick={() => navigate(x)} zamienionych automatycznie
- 5 skrotow panelu admina (setTab) -> Link do /admin/*
- karty narzedzi konta i kafelek poradnika: zamiast role="button" prawdziwy link
- pozycje menu konta i popovera powiadomien -> Link + zamkniecie popovera
- karty ofert: link rozciagniety na cala karte (.card-stretched-link) zamiast
role="button", bez zagniezdzania przyciskow w <a>
- bez zmian zostaly przyciski wykonujace akcje: publikacja, zapis draftu,
wyszukiwanie, oznaczanie powiadomien, bramki logowania
Wyglad 1:1 - rozszerzone selektory kontenerow o warianty <a>, reset :where(a)
o zerowej specyficznosci oraz odtworzenie centrowania tam, gdzie <button>
robil to domyslnie. Zweryfikowane zrzutami ekranu i pomiarem geometrii.
Naprawa: kazdy blad /auth/me (takze chwilowy timeout albo 5xx) kasowal token
i wylogowywal uzytkownika. Teraz sesje konczy tylko odrzucenie tokenu (401/403),
pozostale bledy sa ponawiane raz.
Testy: frontend/tests/routing.spec.ts (41 testow) + playwright.config.ts,
skrypt npm run test:e2e. E2E_BASE_URL pozwala uruchomic je na buildzie
produkcyjnym za nginx.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- AGENTS.md: obowiazkowy standard architektoniczny - kazdy widok ma wlasny
URL w React Routerze, zakaz nawigacji na useState/setView/activeView,
identyfikatory w parametrach URL, filtry w query string, ProtectedRoute
- 7 z 9 atrap href="#" zamienionych na <Link> do istniejacych tras
- pozostaly 2 w ServicesPage ("Zobacz firmy", "Zobacz wszystkie") - brak
docelowej strony listy firm, wymagaja decyzji produktowej
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 16 przyciskow w breadcrumbach zamienionych na <Link> (semantyka linku,
otwieranie w nowej karcie, podglad celu na hover)
- naglowek: "Moje konto" i "Dodaj ogloszenie za darmo" jako <Link>
- CSS breadcrumbow rozszerzony o warianty <a>, wyglad bez zmian
(zweryfikowane zrzutami ekranu)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- nowa trasa startuje od gory strony, przy cofaniu (POP) pozycja zostaje
przy przegladarce, wiec powrot do listy ofert wraca w to samo miejsce
- AccountComparisonPage: setSelectedDetails({}) tworzylo nowy obiekt przy
kazdym przebiegu efektu zaleznego od selectedDetails, co dawalo
nieskonczona petle ("Maximum update depth exceeded"). Blad byl obecny
przed migracja - ujawnily go testy E2E nowej trasy /konto/porownanie.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- aktywna zakladka wyznaczana z adresu (useParams), nie z useState
- setTab() nawiguje zamiast zmieniac stan lokalny
- sidebar admina: <button> -> <NavLink>
- typ AdminTab zunifikowany z AdminTabKey z routes.ts
- nieznana sekcja /admin/cokolwiek -> strona 404
- CSS .admin-nav rozszerzony o warianty <a>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rdzen migracji - kazdy widok ma wlasny adres URL:
- render {view === '...'} zastapiony przez <Routes> z 31 trasami
- usuniete: type View, PROTECTED_VIEWS, proteza #login, stan view/mapCity/
mapOfferType/selectedListingId/messageThreadTarget
- prop onNavigate (166 wystapien) zastapiony hookiem useNavigate w komponentach
- /oferta/:id czyta identyfikator z adresu (ListingDetailRoute)
- /mapa?miasto=&typ= czyta parametry z query stringa (MapSearchRoute)
- dane przejsciowe miedzy trasami (watek wiadomosci, oferta do negocjacji)
przekazywane w location.state zamiast globalnego stanu
- ProtectedRoute na trasach konta, requireAdmin na /admin
- powrot do zamierzonego celu po zalogowaniu (location.state.from)
- Header i AccountSidebar: <button> -> <NavLink>, aktywnosc z adresu
- strona 404 dla nieznanych adresow
- CSS: selektory menu rozszerzone o warianty <a> (wyglad bez zmian)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fundament migracji z useState(view) na prawdziwy routing:
- react-router-dom 7 w zaleznosciach
- BrowserRouter w main.tsx (nginx ma juz try_files, wiec czyste sciezki dzialaja)
- routes.ts: centralna mapa adresow + helpery listingPath/mapPath/adminTabPath
- ProtectedRoute z obsluga stanu loading z AuthProvider
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Kalkulator zdolności kredytowej: nowy widok z live-obliczeniami (rata
annuitetowa/malejąca, DTI/DSTI, rekomendacje, donut), wpięty w poradnik,
panel i stronę główną; pasek boczny konta dla zalogowanych.
- Moje oferty: prawdziwe ogłoszenia (/listings/mine) scalone z listą,
realne akcje Wstrzymaj/Wznów (PATCH /status, nowy status PAUSED),
Usuń (DELETE) z kontrolą właściciela; realne wyświetlenia (viewsCount)
i zapisane z ulubionych. Fix CHECK constraintu enuma w SchemaFixer.
- System powiadomień: encja Notification + serwis + kontroler + SSE
realtime (token w query param w JwtAuthFilter). Podpięte źródła
serwerowe (wiadomości, zmiana statusu ogłoszenia, zdarzenia konta)
i klienckie (ulubione, spadek ceny, spotkania, weryfikacja telefonu).
Centralny NotificationsProvider; dzwonek i strona bez zmian designu,
klikalne z deep-linkami i oznaczaniem jako przeczytane.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>