This commit is contained in:
+65
-1
@@ -14240,6 +14240,7 @@ function AddListingPage({ editListingId }: { editListingId?: number }) {
|
||||
// dalej, zeby nie krzyczec o brakach zanim uzytkownik cokolwiek wypelni.
|
||||
const [showDetailsError, setShowDetailsError] = useState(false);
|
||||
const [showLocationError, setShowLocationError] = useState(false);
|
||||
const [showPhotosError, setShowPhotosError] = useState(false);
|
||||
const [listingDetails, setListingDetails] = useState(INITIAL_LISTING_DETAILS);
|
||||
// Ekran startowy z wyborem "Chcę sprzedać / wynająć" + kategorii. Przy edycji pomijamy go i od razu
|
||||
// pokazujemy formularz. Wybor kategorii ustawia transakcje i rodzaj, a potem wchodzimy w kreator.
|
||||
@@ -14640,8 +14641,17 @@ function AddListingPage({ editListingId }: { editListingId?: number }) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Krok "Zdjęcia" (currentStep 2) - wymagamy przynajmniej jednego zdjecia, zeby przejsc dalej.
|
||||
if (currentStep === 2 && step > 2 && photos.length === 0) {
|
||||
setShowPhotosError(true);
|
||||
window.requestAnimationFrame(() => {
|
||||
document.querySelector('.add-step-error')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
});
|
||||
return;
|
||||
}
|
||||
setShowDetailsError(false);
|
||||
setShowLocationError(false);
|
||||
setShowPhotosError(false);
|
||||
setCurrentStep(step);
|
||||
window.requestAnimationFrame(() => {
|
||||
document.querySelector('.add-page')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
@@ -15328,6 +15338,12 @@ function AddListingPage({ editListingId }: { editListingId?: number }) {
|
||||
{virtualTourError && <p className="photo-tour-error" role="alert">{virtualTourError}</p>}
|
||||
</div>
|
||||
|
||||
{showPhotosError && photos.length === 0 && (
|
||||
<p className="add-field-error add-step-error" role="alert">
|
||||
Dodaj przynajmniej jedno zdjęcie, aby przejść dalej.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="add-location-actions photos-actions">
|
||||
<button className="back-step-button" type="button" onClick={() => goToStep(1)}>
|
||||
Wstecz
|
||||
@@ -22518,6 +22534,45 @@ function ResultCardGallery({ photos, cover, alt }: { photos: string[]; cover: st
|
||||
);
|
||||
}
|
||||
|
||||
// Animacja wczytywania listy ofert - pokazywana zamiast pustego miejsca, dopoki backend nie odda
|
||||
// ogloszen. Znika sama, gdy dane przyjda (komponent nadrzedny przestaje ja renderowac).
|
||||
function ListingsLoader() {
|
||||
return (
|
||||
<div className="listings-loader" role="status" aria-live="polite">
|
||||
<div className="listings-loader-art" aria-hidden="true">
|
||||
<svg viewBox="0 0 240 150" width="200" height="125" role="img">
|
||||
<path d="M42 118a78 78 0 0 1 156 0Z" fill="#e8f5ed" />
|
||||
<g className="ll-cloud ll-cloud-a" fill="#eef2f6">
|
||||
<ellipse cx="58" cy="44" rx="17" ry="9" />
|
||||
<ellipse cx="70" cy="40" rx="12" ry="8" />
|
||||
</g>
|
||||
<g className="ll-cloud ll-cloud-b" fill="#eef2f6">
|
||||
<ellipse cx="188" cy="52" rx="15" ry="8" />
|
||||
<ellipse cx="177" cy="48" rx="10" ry="7" />
|
||||
</g>
|
||||
<g>
|
||||
<rect x="58" y="96" width="7" height="22" rx="3" fill="#c3ddca" />
|
||||
<circle cx="61.5" cy="90" r="16" fill="#d4ead9" />
|
||||
</g>
|
||||
<g className="ll-house">
|
||||
<rect x="96" y="86" width="60" height="34" rx="3" fill="#ffffff" stroke="#cadfd2" strokeWidth="2.5" />
|
||||
<path d="M126 58l34 30H92Z" fill="#d4ead9" stroke="#9ecfae" strokeWidth="2.5" strokeLinejoin="round" />
|
||||
<rect x="150" y="66" width="8" height="16" rx="2" fill="#c3ddca" />
|
||||
<rect x="118" y="98" width="16" height="22" rx="2" fill="#38b26a" />
|
||||
<circle cx="130" cy="109" r="1.6" fill="#ffffff" />
|
||||
</g>
|
||||
<rect x="42" y="117" width="156" height="3" rx="1.5" fill="#d9ebe0" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="listings-loader-title">Wczytujemy <span>ogłoszenia</span>…</p>
|
||||
<p className="listings-loader-sub">Zaraz pokażemy Ci najlepsze oferty dopasowane do Twoich kryteriów.</p>
|
||||
<div className="listings-loader-bars" aria-hidden="true">
|
||||
<span /><span /><span /><span />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RealListingsBand({
|
||||
offerType,
|
||||
onOpenListing,
|
||||
@@ -22553,7 +22608,16 @@ function RealListingsBand({
|
||||
const cityFilter = (city ?? '').trim();
|
||||
const visible = cityFilter ? items.filter((item) => cityStartsWith(item.city, cityFilter)) : items;
|
||||
|
||||
if (loading || visible.length === 0) {
|
||||
// W trakcie ladowania pokazujemy animacje zamiast pustego miejsca; gdy dane przyjda i lista jest
|
||||
// pusta - nie pokazujemy nic (jak dotad).
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="real-listings-section" aria-label="Ogłoszenia dodane w serwisie">
|
||||
<ListingsLoader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (visible.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -26568,6 +26568,107 @@ a.city-card:hover {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
/* Animacja wczytywania ofert - wypelnia miejsce listy, dopoki backend nie odda ogloszen. */
|
||||
.listings-loader {
|
||||
align-items: center;
|
||||
background: linear-gradient(180deg, #f6faf7 0%, #ffffff 100%);
|
||||
border: 1px solid #e7eef0;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-top: 18px;
|
||||
padding: 30px 24px 34px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.listings-loader-art {
|
||||
animation: ll-float 2.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.ll-house {
|
||||
animation: ll-pop 2.6s ease-in-out infinite;
|
||||
transform-origin: 126px 118px;
|
||||
}
|
||||
|
||||
.ll-cloud-a { animation: ll-drift 5s ease-in-out infinite; }
|
||||
.ll-cloud-b { animation: ll-drift 6.5s ease-in-out infinite reverse; }
|
||||
|
||||
.listings-loader-title {
|
||||
color: #13243d;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
|
||||
.listings-loader-title span { color: #10a15e; }
|
||||
|
||||
.listings-loader-sub {
|
||||
color: #7b8896;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listings-loader-bars {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.listings-loader-bars span {
|
||||
background: #e4ebe6;
|
||||
border-radius: 999px;
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
width: 46px;
|
||||
}
|
||||
|
||||
.listings-loader-bars span::after {
|
||||
animation: ll-fill 1.4s ease-in-out infinite;
|
||||
background: #10a15e;
|
||||
border-radius: inherit;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
transform: translateX(-100%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.listings-loader-bars span:nth-child(2)::after { animation-delay: 0.18s; }
|
||||
.listings-loader-bars span:nth-child(3)::after { animation-delay: 0.36s; }
|
||||
.listings-loader-bars span:nth-child(4)::after { animation-delay: 0.54s; }
|
||||
|
||||
@keyframes ll-float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-6px); }
|
||||
}
|
||||
|
||||
@keyframes ll-pop {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.04); }
|
||||
}
|
||||
|
||||
@keyframes ll-drift {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
50% { transform: translateX(6px); }
|
||||
}
|
||||
|
||||
@keyframes ll-fill {
|
||||
0% { transform: translateX(-100%); }
|
||||
55%, 100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.listings-loader-art,
|
||||
.ll-house,
|
||||
.ll-cloud-a,
|
||||
.ll-cloud-b,
|
||||
.listings-loader-bars span::after {
|
||||
animation: none;
|
||||
}
|
||||
.listings-loader-bars span::after { transform: translateX(0); }
|
||||
}
|
||||
|
||||
.real-listings-heading {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -30956,6 +31057,19 @@ a.listing-detail-back {
|
||||
}
|
||||
|
||||
/* Widok KOMPAKTOWY: gestsze wiersze, mniejsze zdjecie, bez metadanych. */
|
||||
/* Widok LISTA (domyslny): karty nieco wieksze niz bazowe - szersze zdjecie i wyzszy kafelek.
|
||||
Tylko od 761px w gore, zeby nie ruszac ukladu mobilnego (ponizej 760px karty i tak sie skladaja). */
|
||||
@media (min-width: 761px) {
|
||||
.result-list.view-lista .result-card {
|
||||
grid-template-columns: 320px minmax(0, 1fr) 190px;
|
||||
min-height: 232px;
|
||||
}
|
||||
|
||||
.result-list.view-lista .result-photo {
|
||||
min-height: 232px;
|
||||
}
|
||||
}
|
||||
|
||||
.result-list.view-kompaktowa { gap: 8px; }
|
||||
|
||||
.result-list.view-kompaktowa .result-card {
|
||||
|
||||
Reference in New Issue
Block a user