Strona /firmy, CTA jako linki i testy Playwright routingu

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>
This commit is contained in:
2026-07-24 00:37:04 +02:00
parent e1ab61ec7c
commit 3e394998a3
9 changed files with 796 additions and 148 deletions
+165 -17
View File
@@ -589,7 +589,7 @@ svg {
width: max-content;
}
.search-tabs button {
.search-tabs button, .search-tabs a {
background: rgba(255, 255, 255, 0.88);
border: 0;
color: #657284;
@@ -600,11 +600,11 @@ svg {
padding: 10px 20px;
}
.search-tabs button:first-child {
.search-tabs button:first-child, .search-tabs a:first-child {
border-radius: 6px 0 0 0;
}
.search-tabs button:last-child {
.search-tabs button:last-child, .search-tabs a:last-child {
border-radius: 0 6px 0 0;
}
@@ -798,7 +798,7 @@ svg {
font-size: 12px;
}
.tool-card button {
.tool-card button, .tool-card a {
align-self: end;
background: #f8fafc;
border: 1px solid #e2e8ef;
@@ -3183,7 +3183,7 @@ svg {
margin-top: 6px;
}
.result-side button {
.result-side button, .result-side a {
align-self: end;
background: #0b9f5c;
border: 0;
@@ -5922,7 +5922,7 @@ svg {
padding: 15px 16px;
}
.sell-valuation-card button {
.sell-valuation-card button, .sell-valuation-card a {
background: #0b9f5c;
border: 0;
border-radius: 6px;
@@ -8331,7 +8331,7 @@ svg {
margin-top: 3px;
}
.sell-bottom-cta button {
.sell-bottom-cta button, .sell-bottom-cta a {
background: #0b9f5c;
border: 0;
border-radius: 6px;
@@ -11417,7 +11417,7 @@ svg {
width: 100%;
}
.search-tabs button {
.search-tabs button, .search-tabs a {
flex: 1;
min-width: 0;
padding-left: 10px;
@@ -18271,7 +18271,7 @@ svg {
gap: 8px;
}
.price-alerts-active-items button {
.price-alerts-active-items button, .price-alerts-active-items a {
align-items: center;
background: #fbfcfe;
border: 1px solid #e2eaf3;
@@ -18405,7 +18405,7 @@ svg {
}
.settings-card-head.split > button,
.settings-profile-row > button {
.settings-profile-row > button, .settings-profile-row > a {
background: #ffffff;
border: 1px solid #d9e2ec;
border-radius: 6px;
@@ -20757,7 +20757,7 @@ svg {
gap: 12px;
}
.public-profile-head > button {
.public-profile-head > button, .public-profile-head > a {
align-items: center;
background: #ffffff;
border: 1px solid #d9e2ec;
@@ -20771,7 +20771,7 @@ svg {
padding: 0 14px;
}
.public-profile-head > button svg {
.public-profile-head > button svg, .public-profile-head > a svg {
height: 14px;
width: 14px;
}
@@ -21559,7 +21559,7 @@ svg {
margin: 0;
}
.admin-card-head button {
.admin-card-head button, .admin-card-head a {
background: transparent;
border: 0;
color: #159e5d;
@@ -21636,7 +21636,7 @@ svg {
padding: 12px 18px 18px;
}
.admin-quick-actions-grid button {
.admin-quick-actions-grid button, .admin-quick-actions-grid a {
align-items: center;
background: #f8fbff;
border: 1px solid #e0e8f2;
@@ -21650,11 +21650,11 @@ svg {
padding: 0 12px;
}
.admin-quick-actions-grid button svg:first-child {
.admin-quick-actions-grid button svg:first-child, .admin-quick-actions-grid a svg:first-child {
color: #109253;
}
.admin-quick-actions-grid button svg:last-child {
.admin-quick-actions-grid button svg:last-child, .admin-quick-actions-grid a svg:last-child {
margin-left: auto;
opacity: 0.6;
}
@@ -22853,7 +22853,7 @@ svg {
}
.listing-detail-back,
.listing-detail-crumbs button {
.listing-detail-crumbs button, .listing-detail-crumbs a {
align-items: center;
background: none;
border: none;
@@ -26789,3 +26789,151 @@ svg {
/* Routing: naglowkowe CTA jako <Link> - zachowanie wygladu przyciskow */
.post-button { align-content: center; text-decoration: none; }
/* Lista firm i uslug (/firmy) - korzysta z istniejacych kart .directory-card */
.companies-frame {
display: block;
margin: 0 auto;
max-width: 1420px;
padding: 0 96px;
}
.companies-frame .services-main { padding-right: 0; }
.companies-head { margin: 18px 0 20px; }
.companies-head h1 {
color: #111a2e;
font-size: 30px;
font-weight: 900;
line-height: 1.15;
margin: 0 0 8px;
}
.companies-head p {
color: #5b6472;
font-size: 15px;
margin: 0 0 10px;
max-width: 720px;
}
.companies-count {
color: #109253;
font-size: 13px;
font-weight: 800;
}
.companies-filters {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 20px;
}
.companies-chip {
align-items: center;
background: #ffffff;
border: 1px solid #dce4ec;
border-radius: 999px;
color: #27384f;
cursor: pointer;
display: inline-flex;
font-size: 13px;
font-weight: 800;
gap: 7px;
padding: 8px 14px;
}
.companies-chip svg { color: #7d8ca0; font-size: 14px; }
.companies-chip span {
background: #eef3f8;
border-radius: 999px;
color: #5b6472;
font-size: 11px;
font-weight: 900;
padding: 1px 7px;
}
.companies-chip:hover { border-color: #b9c6d4; }
.companies-chip.active {
background: #ebf6f0;
border-color: #13a360;
color: #109253;
}
.companies-chip.active svg { color: #109253; }
.companies-chip.active span { background: #d7efe2; color: #0d7a45; }
.companies-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 1280px) {
.companies-frame { padding: 0 24px; }
.companies-list { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
.companies-head h1 { font-size: 24px; }
.companies-frame { padding: 0 14px; }
}
/* Routing: CTA nawigacyjne renderuja sie jako <a> (Link). :where() ma zerowa
specyficznosc, wiec kazda istniejaca regula klasy nadal wygrywa - to tylko
zdejmuje domyslne podkreslenie i niebieski kolor linku. */
:where(a) {
color: inherit;
text-decoration: none;
}
/* Routing: CTA o ukladzie blokowym odtwarzaja centrowanie tresci znane z <button> */
a.sell-primary,
a.account-new-search,
a.price-alerts-link,
a.price-alerts-manage,
a.cover-preview-button,
a.back-step-button,
a.listing-detail-back {
align-items: center;
display: flex;
justify-content: center;
text-align: center;
}
/* Kontenery, ktorych reguly nie ustawialy display - <button> centrowal tresc sam,
<a> wymaga tego jawnie. Pudelko (padding, min-height, tlo) bez zmian. */
.search-tabs a,
.tool-card a,
.sell-valuation-card a,
.sell-bottom-cta a,
.settings-profile-row > a {
align-items: center;
display: flex;
justify-content: center;
text-align: center;
}
/* Karty prowadzace do osobnej strony: link rozciagniety na cala karte.
Daje prawdziwy <a href> (kopiowanie, nowa karta, indeksowanie) bez
zagniezdzania przyciskow w <a>. Kontrolki karty zostaja klikalne nad nakladka. */
.real-result-card { position: relative; }
.card-stretched-link::after {
content: "";
inset: 0;
position: absolute;
z-index: 1;
}
.real-result-card .real-favorite-button,
.real-result-card .real-card-cta {
position: relative;
z-index: 2;
}
.real-card-cta {
align-items: center;
display: inline-flex;
justify-content: center;
text-align: center;
}