diff --git a/.gitignore b/.gitignore
index c3c1758..a3f02be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@ backend/target/
frontend/node_modules/
frontend/dist/
frontend/.vite/
+frontend/test-results/
+frontend/playwright-report/
# IDE
.idea/
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 6c7e162..1553223 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -15,6 +15,7 @@
"react-router-dom": "^7.18.1"
},
"devDependencies": {
+ "@playwright/test": "^1.61.1",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@vitejs/plugin-react": "6.0.2",
@@ -109,6 +110,22 @@
"url": "https://github.com/sponsors/Boshen"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.61.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz",
+ "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.61.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
@@ -991,6 +1008,53 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/playwright": {
+ "version": "1.61.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
+ "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.61.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.61.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
+ "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/png-js": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.1.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index d3d753e..061f929 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,7 +6,8 @@
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "tsc && vite build",
- "preview": "vite preview --host 0.0.0.0"
+ "preview": "vite preview --host 0.0.0.0",
+ "test:e2e": "playwright test"
},
"dependencies": {
"@types/pdfmake": "^0.3.3",
@@ -16,6 +17,7 @@
"react-router-dom": "^7.18.1"
},
"devDependencies": {
+ "@playwright/test": "^1.61.1",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@vitejs/plugin-react": "6.0.2",
diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts
new file mode 100644
index 0000000..51eac2e
--- /dev/null
+++ b/frontend/playwright.config.ts
@@ -0,0 +1,29 @@
+import { defineConfig } from '@playwright/test';
+
+// Testy routingu uruchamiamy na systemowym Chrome (bez pobierania przegladarek Playwright).
+// Domyslnie startujemy wlasny dev server; ustaw E2E_BASE_URL, zeby przetestowac
+// build produkcyjny za nginx, np. E2E_BASE_URL=http://localhost npm run test:e2e
+const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
+
+export default defineConfig({
+ testDir: './tests',
+ timeout: 60_000,
+ expect: { timeout: 15_000 },
+ fullyParallel: false,
+ workers: 1,
+ reporter: [['list']],
+ use: {
+ baseURL,
+ channel: 'chrome',
+ viewport: { width: 1680, height: 1050 },
+ actionTimeout: 15_000,
+ },
+ webServer: process.env.E2E_BASE_URL
+ ? undefined
+ : {
+ command: 'npm run dev',
+ url: 'http://localhost:5173',
+ reuseExistingServer: true,
+ timeout: 120_000,
+ },
+});
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 86d9d35..1d1bb6f 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,7 +1,7 @@
import { Fragment, type ChangeEvent as ReactChangeEvent, type ClipboardEvent as ReactClipboardEvent, type Dispatch, type DragEvent as ReactDragEvent, type FormEvent as ReactFormEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode, type SetStateAction, type WheelEvent as ReactWheelEvent, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import './styles.css';
import { Link, NavLink, Route, Routes, useLocation, useNavigate, useNavigationType, useParams, useSearchParams } from 'react-router-dom';
-import { ROUTES, listingPath, mapPath, negotiationPath, adminTabPath, ADMIN_PATH_TO_TAB, type AdminTabKey } from './routes';
+import { ROUTES, listingPath, mapPath, negotiationPath, companiesPath, adminTabPath, ADMIN_PATH_TO_TAB, type AdminTabKey } from './routes';
import { ProtectedRoute } from './ProtectedRoute';
import { apiFetch, useAuth } from './auth';
import type { AuthUser, ContactPreference, PreferredLanguage } from './auth';
@@ -1510,49 +1510,60 @@ const rentListings: RentOffer[] = [
},
];
+// slug trafia do adresu listy firm: /firmy?kategoria=remonty
const serviceCategories = [
{
+ slug: 'finanse',
icon: 'credit-card',
title: 'Finanse',
text: 'Kredyty hipoteczne, doradcy, ubezpieczenia',
},
{
+ slug: 'prawo',
icon: 'scale',
title: 'Prawo i dokumenty',
text: 'Notariusze, prawnicy, rzeczoznawcy',
},
{
+ slug: 'remonty',
icon: 'paint',
title: 'Remont i wykończenie',
text: 'Firmy remontowe, fachowcy, projekty wnętrz',
},
{
+ slug: 'przeprowadzka',
icon: 'truck',
title: 'Przeprowadzka',
text: 'Firmy przeprowadzkowe, magazyny, transport',
},
{
+ slug: 'wyposazenie',
icon: 'sofa',
title: 'Wyposażenie wnętrz',
text: 'Meble, AGD, kuchnie na wymiar, oświetlenie',
},
{
+ slug: 'sprzedaz',
icon: 'camera',
title: 'Sprzedaż nieruchomości',
text: 'Fotografia, home staging, wirtualne spacery 3D',
},
{
+ slug: 'deweloperzy',
icon: 'city',
title: 'Deweloperzy i inwestycje',
text: 'Nowe inwestycje, deweloperzy, biura sprzedaży',
},
{
+ slug: 'inne',
icon: 'dots',
title: 'Inne usługi',
text: 'Usługi dodatkowe związane z nieruchomościami',
},
];
+type ServiceCategorySlug = (typeof serviceCategories)[number]['slug'];
+
const serviceCompanies = [
{
icon: 'house',
@@ -1611,6 +1622,36 @@ const serviceDirectory = [
},
];
+// Pelna baza firm dla listy /firmy. Kazda pozycja ma kategorie, wiec filtrowanie
+// po ?kategoria= dziala na realnych danych, a nie tylko podswietla zakladke.
+const companyDirectory: Array<{
+ title: string;
+ category: ServiceCategorySlug;
+ rating: string;
+ text: string;
+ city: string;
+ image: string;
+ position: string;
+}> = [
+ { title: 'Finanse Expert', category: 'finanse', rating: '4.9 (128 opinii)', text: 'Kredyty hipoteczne i doradztwo finansowe', city: 'Warszawa, cała Polska', image: cityImage, position: '50% 40%' },
+ { title: 'Kredyt Partner', category: 'finanse', rating: '4.6 (57 opinii)', text: 'Porównanie ofert kredytowych, ubezpieczenia', city: 'Gdańsk, pomorskie', image: heroImage, position: '30% 55%' },
+ { title: 'Notariusz Nowak', category: 'prawo', rating: '4.9 (74 opinie)', text: 'Kancelaria notarialna, akty i umowy', city: 'Wrocław, dolnośląskie', image: heroImage, position: '62% 45%' },
+ { title: 'Lex Nieruchomości', category: 'prawo', rating: '4.7 (41 opinii)', text: 'Obsługa prawna transakcji, księgi wieczyste', city: 'Warszawa, mazowieckie', image: cityImage, position: '70% 60%' },
+ { title: 'Remont Masters', category: 'remonty', rating: '4.8 (96 opinii)', text: 'Kompleksowe remonty mieszkań i domów', city: 'Kraków, małopolskie', image: heroImage, position: '72% 62%' },
+ { title: 'Perfect House', category: 'remonty', rating: '4.7 (64 opinie)', text: 'Wykończenia wnętrz pod klucz', city: 'Warszawa, mazowieckie', image: heroImage, position: '92% 50%' },
+ { title: 'Home Finish', category: 'remonty', rating: '4.6 (52 opinie)', text: 'Remonty, wykończenia, projekty wnętrz', city: 'Wrocław, dolnośląskie', image: heroImage, position: '48% 58%' },
+ { title: 'SuperPrzeprowadzki', category: 'przeprowadzka', rating: '4.7 (111 opinii)', text: 'Przeprowadzki lokalne i krajowe', city: 'Poznań, wielkopolskie', image: cityImage, position: '40% 50%' },
+ { title: 'Transport 24', category: 'przeprowadzka', rating: '4.5 (38 opinii)', text: 'Transport mebli, magazynowanie, pakowanie', city: 'Łódź, łódzkie', image: heroImage, position: '20% 70%' },
+ { title: 'Kuchnie na Wymiar', category: 'wyposazenie', rating: '4.8 (83 opinie)', text: 'Meble kuchenne i zabudowy na wymiar', city: 'Katowice, śląskie', image: heroImage, position: '85% 40%' },
+ { title: 'Salon Oświetlenia Lumo', category: 'wyposazenie', rating: '4.4 (29 opinii)', text: 'Oświetlenie, AGD, wyposażenie wnętrz', city: 'Szczecin, zachodniopomorskie', image: cityImage, position: '60% 35%' },
+ { title: 'FotoNieruchomości', category: 'sprzedaz', rating: '4.9 (67 opinii)', text: 'Fotografia ofert, home staging, spacery 3D', city: 'Warszawa, cała Polska', image: cityImage, position: '35% 65%' },
+ { title: 'Studio Wirtualnych Spacerów', category: 'sprzedaz', rating: '4.7 (45 opinii)', text: 'Wirtualne spacery 3D i plany mieszkań', city: 'Kraków, małopolskie', image: heroImage, position: '55% 48%' },
+ { title: 'Nowa Inwestycja Development', category: 'deweloperzy', rating: '4.6 (58 opinii)', text: 'Nowe inwestycje mieszkaniowe, biuro sprzedaży', city: 'Gdynia, pomorskie', image: cityImage, position: '80% 55%' },
+ { title: 'Osiedle Park Deweloper', category: 'deweloperzy', rating: '4.5 (34 opinie)', text: 'Mieszkania z rynku pierwotnego', city: 'Wrocław, dolnośląskie', image: heroImage, position: '45% 42%' },
+ { title: 'Serwis Domowy Fix', category: 'inne', rating: '4.6 (72 opinie)', text: 'Drobne naprawy, złota rączka, przeglądy', city: 'Cała Polska', image: heroImage, position: '68% 68%' },
+ { title: 'Czysto i Gotowe', category: 'inne', rating: '4.8 (91 opinii)', text: 'Sprzątanie po remoncie i przed wprowadzeniem', city: 'Warszawa, mazowieckie', image: cityImage, position: '25% 45%' },
+];
+
const serviceSteps = [
{
icon: 'group',
@@ -3431,6 +3472,7 @@ function App() {
Brak ogłoszeń do wyświetlenia. Brak użytkowników do wyświetlenia. Nasi eksperci pomogą Ci przejść przez proces zakupu i wynegocjować najlepszą cenę. Nie masz jeszcze zapisanych wyszukiwań. Wejdź w Kup lub Wynajem i kliknij dzwonek „Zapisz wyszukiwanie”. Wróć do swoich filtrów jednym kliknięciem i przejdź od razu do odpowiedniej listy ofert. Ustaw filtry w Kup lub Wynajem i kliknij dzwonek „Zapisz wyszukiwanie”, a pojawią się tutaj. Zobacz, jak Twój profil prezentuje się publicznie dla innych użytkowników Mieszko. {card.text}Ostatnie ogłoszenia
-
+ Zobacz wszystkie Aktywność użytkowników
-
+ Zobacz wszystkich Szybkie akcje
Moje wyszukiwania
-
+ Zobacz wszystkie Zapisane wyszukiwania
Jak inni widzą Twój profil
{card.title}
Przeglądaj aktualne oferty dopasowane do Twoich potrzeb i budżetu.
{topic.text}
{topic.count} + > + ); + + return isBuying ? ( + + {content} + + ) : ( +Bezpłatna wycena online w 30 sekund
+ {activeCategory + ? activeCategory.text + : 'Sprawdzone firmy i specjaliści z całej Polski. Porównaj oferty, oceny i opinie klientów.'} +
+ + {companies.length === 1 ? '1 firma' : `${companies.length} firm`} + {activeCategory ? ` w kategorii „${activeCategory.title}”` : ' w bazie'} + +{step.text}
+{category.text}
- Zobacz firmy{[item.city, item.district].filter(Boolean).join(', ')}
{meta}{error ?? 'Nie znaleziono ogłoszenia.'}
-