This commit is contained in:
+503
-18
@@ -131,6 +131,10 @@ export type ApiListingDetail = {
|
||||
ownerAccountType: AccountType | null;
|
||||
// Nazwa wlasciciela - uzywana jako etykieta oferty firmowej.
|
||||
ownerName: string | null;
|
||||
// Data zalozenia konta wlasciciela ("Na Polska Lokalnie od...") - null, gdy ogloszenie nie ma konta.
|
||||
ownerCreatedAt: string | null;
|
||||
// Ostatnia aktywnosc wlasciciela ("Ostatnio aktywny/a...") - null, gdy ogloszenie nie ma konta.
|
||||
ownerLastActiveAt: string | null;
|
||||
status: ApiListingStatus;
|
||||
createdAt: string;
|
||||
viewsCount?: number;
|
||||
@@ -8111,6 +8115,41 @@ function priceAlertRelativeTime(iso: string): string {
|
||||
return days === 1 ? 'wczoraj' : `${days} dni temu`;
|
||||
}
|
||||
|
||||
// "W serwisie od sierpień 2026" - miesiac i rok zalozenia konta sprzedajacego. Puste, gdy ogloszenie
|
||||
// nie ma konta w serwisie (dane demonstracyjne) albo data jest nieczytelna.
|
||||
function sellerMemberSince(iso: string | null): string {
|
||||
if (!iso) {
|
||||
return '';
|
||||
}
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return '';
|
||||
}
|
||||
return `W serwisie od ${date.toLocaleDateString('pl-PL', { month: 'long', year: 'numeric' })}`;
|
||||
}
|
||||
|
||||
// "Ostatnia aktywność: 2 godz. temu"; do 5 minut od aktywnosci pokazujemy "teraz online". Sformulowanie
|
||||
// bez rodzaju gramatycznego, bo dziala tak samo dla osoby, jak i dla firmy.
|
||||
function sellerLastActive(iso: string | null): string {
|
||||
if (!iso) {
|
||||
return '';
|
||||
}
|
||||
const time = new Date(iso).getTime();
|
||||
if (Number.isNaN(time)) {
|
||||
return '';
|
||||
}
|
||||
const minutes = Math.max(0, Math.round((Date.now() - time) / 60000));
|
||||
if (minutes < 5) {
|
||||
return 'Aktywność: teraz online';
|
||||
}
|
||||
return `Ostatnia aktywność: ${priceAlertRelativeTime(iso)}`;
|
||||
}
|
||||
|
||||
// Czytelna etykieta adresu www - bez schematu i koncowego ukosnika (np. "www.matexi.pl").
|
||||
function companyWebsiteLabel(url: string): string {
|
||||
return url.replace(/^https?:\/\//i, '').replace(/\/$/, '');
|
||||
}
|
||||
|
||||
// Procentowa zmiana miedzy dwiema cenami; brak punktu odniesienia = 0%.
|
||||
function priceAlertChangePercent(previous: number, current: number): number {
|
||||
return previous > 0 ? ((current - previous) / previous) * 100 : 0;
|
||||
@@ -11323,6 +11362,18 @@ function AccountSettingsPage({ onSearchPreferencesSaved }: { onSearchPreferences
|
||||
}));
|
||||
const [identityBanner, setIdentityBanner] = useState<Banner>(null);
|
||||
const [isSavingIdentity, setIsSavingIdentity] = useState(false);
|
||||
// Dane firmowe pokazywane na profilu publicznym (tylko konto COMPANY). Ladowane z ustawien konta.
|
||||
const [companyProfile, setCompanyProfile] = useState({
|
||||
companyTagline: '',
|
||||
website: '',
|
||||
socialLinkedin: '',
|
||||
socialFacebook: '',
|
||||
socialInstagram: '',
|
||||
socialYoutube: '',
|
||||
publicContactVisible: false,
|
||||
});
|
||||
const [companyBanner, setCompanyBanner] = useState<Banner>(null);
|
||||
const [isSavingCompany, setIsSavingCompany] = useState(false);
|
||||
const [isGusLoading, setIsGusLoading] = useState(false);
|
||||
const [isSuggestingNick, setIsSuggestingNick] = useState(false);
|
||||
const [isPhoneTouched, setIsPhoneTouched] = useState(false);
|
||||
@@ -11455,6 +11506,15 @@ function AccountSettingsPage({ onSearchPreferencesSaved }: { onSearchPreferences
|
||||
setSettings(loaded);
|
||||
setConsent({ emailConsent: marketing.emailConsent, smsConsent: marketing.smsConsent });
|
||||
setIdentity((current) => ({ ...current, bio: loaded.bio ?? '' }));
|
||||
setCompanyProfile({
|
||||
companyTagline: loaded.companyTagline ?? '',
|
||||
website: loaded.website ?? '',
|
||||
socialLinkedin: loaded.socialLinkedin ?? '',
|
||||
socialFacebook: loaded.socialFacebook ?? '',
|
||||
socialInstagram: loaded.socialInstagram ?? '',
|
||||
socialYoutube: loaded.socialYoutube ?? '',
|
||||
publicContactVisible: loaded.publicContactVisible ?? false,
|
||||
});
|
||||
setSearchPrefs({
|
||||
locations: loaded.searchLocations ?? '',
|
||||
propertyType: loaded.searchPropertyType ?? '',
|
||||
@@ -11665,6 +11725,28 @@ function AccountSettingsPage({ onSearchPreferencesSaved }: { onSearchPreferences
|
||||
}
|
||||
};
|
||||
|
||||
// Zapis danych firmowych profilu publicznego (haslo, www, social media, publiczny kontakt).
|
||||
const saveCompanyProfile = async () => {
|
||||
setIsSavingCompany(true);
|
||||
setCompanyBanner(null);
|
||||
try {
|
||||
await patchSettings({
|
||||
companyTagline: companyProfile.companyTagline.trim(),
|
||||
website: companyProfile.website.trim(),
|
||||
socialLinkedin: companyProfile.socialLinkedin.trim(),
|
||||
socialFacebook: companyProfile.socialFacebook.trim(),
|
||||
socialInstagram: companyProfile.socialInstagram.trim(),
|
||||
socialYoutube: companyProfile.socialYoutube.trim(),
|
||||
publicContactVisible: companyProfile.publicContactVisible,
|
||||
});
|
||||
flashBanner('company', setCompanyBanner, { text: 'Dane firmy zapisane.', tone: 'success' });
|
||||
} catch (error) {
|
||||
setCompanyBanner({ text: error instanceof Error ? error.message : 'Nie udało się zapisać danych firmy.', tone: 'error' });
|
||||
} finally {
|
||||
setIsSavingCompany(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageUpload = async (event: ReactChangeEvent<HTMLInputElement>, target: SettingsUploadTarget) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) {
|
||||
@@ -12237,6 +12319,88 @@ function AccountSettingsPage({ onSearchPreferencesSaved }: { onSearchPreferences
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* --- Profil firmy: dane pokazywane publicznie na rozbudowanym profilu (tylko COMPANY) --- */}
|
||||
{isCompany && (
|
||||
<section className="settings-card settings-card-wide" aria-label="Profil firmy">
|
||||
<div className="settings-card-head">
|
||||
<h2>Profil firmy</h2>
|
||||
<p>Te dane widać na publicznym profilu Twojej firmy. Wszystkie pola są opcjonalne.</p>
|
||||
</div>
|
||||
|
||||
{companyBanner && <p className={`settings-banner ${companyBanner.tone}`} role="status">{companyBanner.text}</p>}
|
||||
|
||||
<div className="profile-form-grid">
|
||||
<label className="profile-form-wide">
|
||||
<span>Hasło firmy</span>
|
||||
<input
|
||||
value={companyProfile.companyTagline}
|
||||
maxLength={80}
|
||||
placeholder="np. Deweloper mieszkaniowy"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, companyTagline: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
<label className="profile-form-wide">
|
||||
<span>Strona www</span>
|
||||
<input
|
||||
value={companyProfile.website}
|
||||
placeholder="np. www.twojafirma.pl"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, website: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>LinkedIn</span>
|
||||
<input
|
||||
value={companyProfile.socialLinkedin}
|
||||
placeholder="Link do profilu LinkedIn"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, socialLinkedin: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Facebook</span>
|
||||
<input
|
||||
value={companyProfile.socialFacebook}
|
||||
placeholder="Link do profilu Facebook"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, socialFacebook: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Instagram</span>
|
||||
<input
|
||||
value={companyProfile.socialInstagram}
|
||||
placeholder="Link do profilu Instagram"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, socialInstagram: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>YouTube</span>
|
||||
<input
|
||||
value={companyProfile.socialYoutube}
|
||||
placeholder="Link do kanału YouTube"
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, socialYoutube: event.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label className="settings-toggle-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={companyProfile.publicContactVisible}
|
||||
onChange={(event) => setCompanyProfile((current) => ({ ...current, publicContactVisible: event.target.checked }))}
|
||||
/>
|
||||
<span>
|
||||
<strong>Pokazuj telefon i adres na profilu</strong>
|
||||
<small>Numer i adres z sekcji „Moje dane” będą widoczne publicznie (numer po kliknięciu „Pokaż numer”).</small>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div className="settings-card-actions">
|
||||
<button type="button" className="primary" onClick={() => { void saveCompanyProfile(); }} disabled={isSavingCompany}>
|
||||
{isSavingCompany ? 'Zapisywanie...' : 'Zapisz dane firmy'}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* --- Preferencje wyszukiwania: realne pola zapisywane na koncie --- */}
|
||||
<section className="settings-card" aria-label="Preferencje wyszukiwania">
|
||||
<div className="settings-card-head">
|
||||
@@ -24657,6 +24821,18 @@ type PublicProfile = {
|
||||
accountType: AccountType;
|
||||
verified: boolean;
|
||||
memberSince: string;
|
||||
// Ostatnia aktywnosc wlasciciela profilu - null, gdy nieodnotowana.
|
||||
lastActiveAt: string | null;
|
||||
// Dane firmowe (konto COMPANY) - null dla kont prywatnych albo gdy nieuzupelnione.
|
||||
companyTagline: string | null;
|
||||
website: string | null;
|
||||
socialLinkedin: string | null;
|
||||
socialFacebook: string | null;
|
||||
socialInstagram: string | null;
|
||||
socialYoutube: string | null;
|
||||
// Telefon i adres - tylko gdy firma wlaczyla publiczny kontakt; inaczej null.
|
||||
publicPhone: string | null;
|
||||
publicAddress: string | null;
|
||||
// Profil prywatny ogladany przez kogos innego niz wlasciciel - reszta pol jest wtedy pusta.
|
||||
privateProfile: boolean;
|
||||
listingsCount: number;
|
||||
@@ -24676,6 +24852,255 @@ type PublicProfile = {
|
||||
}[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Rozbudowany profil konta firmowego (deweloper/agencja) pod /profil/:id oraz /u/:nick. W odroznieniu
|
||||
* od profilu osoby prywatnej pokazuje baner z logo, dane firmy (haslo, www, social media, opcjonalny
|
||||
* kontakt), statystyki policzone z realnych ogloszen oraz zakladki. Zakladki trzymamy w query stringu
|
||||
* (?sekcja=), zgodnie ze standardem routingu - adres da sie skopiowac, odswiezyc i cofnac.
|
||||
*/
|
||||
function CompanyProfile({ profile }: { profile: PublicProfile }) {
|
||||
const { formatPrice, formatArea } = useDisplay();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [phoneRevealed, setPhoneRevealed] = useState(false);
|
||||
|
||||
const TABS = [
|
||||
{ key: 'o-firmie', label: 'O firmie' },
|
||||
{ key: 'ogloszenia', label: `Ogłoszenia ${profile.listingsCount}` },
|
||||
{ key: 'kontakt', label: 'Kontakt' },
|
||||
];
|
||||
const activeTab = searchParams.get('sekcja') ?? 'o-firmie';
|
||||
const changeTab = (key: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const next = new URLSearchParams(prev);
|
||||
if (key === 'o-firmie') {
|
||||
next.delete('sekcja');
|
||||
} else {
|
||||
next.set('sekcja', key);
|
||||
}
|
||||
return next;
|
||||
}, { replace: false });
|
||||
};
|
||||
|
||||
const memberSinceLabel = new Date(profile.memberSince).toLocaleDateString('pl-PL', { month: 'long', year: 'numeric' });
|
||||
const lastActiveLabel = sellerLastActive(profile.lastActiveAt);
|
||||
// Statystyki wylacznie z realnych ogloszen firmy - zadnych zmyslonych liczb.
|
||||
const forSaleCount = profile.listings.filter((listing) => listing.offerType === 'SALE').length;
|
||||
const forRentCount = profile.listings.filter((listing) => listing.offerType === 'RENT').length;
|
||||
const cityCount = new Set(
|
||||
profile.listings.map((listing) => listing.city.trim().toLowerCase()).filter(Boolean),
|
||||
).size;
|
||||
|
||||
const socialLinks = [
|
||||
{ key: 'linkedin', url: profile.socialLinkedin, label: 'LinkedIn' },
|
||||
{ key: 'facebook', url: profile.socialFacebook, label: 'Facebook' },
|
||||
{ key: 'instagram', url: profile.socialInstagram, label: 'Instagram' },
|
||||
{ key: 'youtube', url: profile.socialYoutube, label: 'YouTube' },
|
||||
].filter((item): item is { key: string; url: string; label: string } => Boolean(item.url));
|
||||
|
||||
const hasContactData = Boolean(profile.website || profile.publicPhone || profile.publicAddress);
|
||||
|
||||
const renderListingCard = (listing: PublicProfile['listings'][number]) => (
|
||||
<Link className="public-listing-card" key={listing.id} to={listingPath(listing.id)}>
|
||||
<div
|
||||
className="public-listing-photo"
|
||||
style={{ backgroundImage: `url(${listing.coverPhoto || heroImage})`, backgroundPosition: 'center center' }}
|
||||
>
|
||||
<span className={`public-listing-badge ${listing.offerType === 'RENT' ? 'rent' : 'sale'}`}>
|
||||
{listing.offerType === 'RENT' ? 'Na wynajem' : 'Na sprzedaż'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="public-listing-body">
|
||||
<strong>{formatPrice(listing.price)}{listing.offerType === 'RENT' ? ' / mies.' : ''}</strong>
|
||||
<h3>{listing.title}</h3>
|
||||
<small><Icon name="pin" /> {[listing.city, listing.district].filter(Boolean).join(', ')}</small>
|
||||
<SpecTiles
|
||||
className="compact"
|
||||
items={[
|
||||
...(listing.area !== null ? [{ icon: 'area', value: formatArea(listing.area), caption: 'Powierzchnia' }] : []),
|
||||
...(listing.rooms !== null ? [{ icon: 'plan', value: `${listing.rooms}`, caption: listing.rooms === 1 ? 'Pokój' : 'Pokoje' }] : []),
|
||||
...(listing.floor ? [{ icon: 'stairs', value: `${listingFloorText(listing.floor, listing.buildingFloors)}`, caption: 'Piętro' }] : []),
|
||||
]}
|
||||
/>
|
||||
<div className="public-listing-foot">
|
||||
<em>{listingRelativeDate(listing.createdAt) || 'Dodane niedawno'}</em>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
||||
// Karta "Szybki kontakt" - pokazywana obok zakladek "O firmie" i "Kontakt". Zawiera wylacznie
|
||||
// realnie dzialajace elementy: strone www, telefon (z odslanianiem numeru) i adres.
|
||||
const contactCard = (
|
||||
<aside className="company-profile-contact-card">
|
||||
<h2>Szybki kontakt</h2>
|
||||
{hasContactData ? (
|
||||
<>
|
||||
<p>Masz pytania? Skontaktuj się z firmą.</p>
|
||||
<ul className="company-contact-list">
|
||||
{profile.publicPhone && (
|
||||
<li>
|
||||
<Icon name="phone" />
|
||||
{phoneRevealed ? (
|
||||
<a href={`tel:${profile.publicPhone.replace(/\s+/g, '')}`}>{profile.publicPhone}</a>
|
||||
) : (
|
||||
<button type="button" className="company-phone-reveal" onClick={() => setPhoneRevealed(true)}>
|
||||
Pokaż numer
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
{profile.publicAddress && (
|
||||
<li><Icon name="pin" /> <span>{profile.publicAddress}</span></li>
|
||||
)}
|
||||
</ul>
|
||||
{profile.website && (
|
||||
<a className="company-contact-www" href={profile.website} target="_blank" rel="noopener noreferrer nofollow">
|
||||
<Icon name="globe" /> Zobacz stronę www
|
||||
</a>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<p className="company-contact-empty">
|
||||
Aby napisać do firmy, otwórz jedno z jej ogłoszeń i użyj przycisku „Napisz wiadomość”.
|
||||
</p>
|
||||
)}
|
||||
</aside>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="company-profile">
|
||||
<section className="company-profile-header">
|
||||
<div
|
||||
className="company-profile-banner"
|
||||
style={{ backgroundImage: `url(${profile.coverImage || cityImage})` }}
|
||||
/>
|
||||
<div className="company-profile-head-row">
|
||||
<div className="company-profile-logo">
|
||||
<Avatar className="company-profile-logo-img" name={profile.fullName} image={profile.avatarImage} singleLetter />
|
||||
</div>
|
||||
<div className="company-profile-identity">
|
||||
{profile.verified && (
|
||||
<span className="company-verified-badge"><Icon name="shield" /> Zweryfikowana firma</span>
|
||||
)}
|
||||
<h1>{profile.fullName}</h1>
|
||||
{profile.companyTagline && <p className="company-profile-tagline">{profile.companyTagline}</p>}
|
||||
<div className="company-profile-quicklinks">
|
||||
{profile.publicAddress && <span><Icon name="pin" /> {profile.publicAddress}</span>}
|
||||
{profile.publicPhone && (
|
||||
<span>
|
||||
<Icon name="phone" />
|
||||
{phoneRevealed ? profile.publicPhone : (
|
||||
<button type="button" className="company-phone-reveal" onClick={() => setPhoneRevealed(true)}>Pokaż numer</button>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{profile.website && (
|
||||
<a href={profile.website} target="_blank" rel="noopener noreferrer nofollow"><Icon name="globe" /> {companyWebsiteLabel(profile.website)}</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{socialLinks.length > 0 && (
|
||||
<div className="company-profile-socials">
|
||||
{socialLinks.map((item) => (
|
||||
<a key={item.key} href={item.url} target="_blank" rel="noopener noreferrer nofollow" aria-label={item.label} title={item.label}>
|
||||
<Icon name={item.key} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<nav className="company-profile-tabs" aria-label="Sekcje profilu firmy">
|
||||
{TABS.map((tab) => (
|
||||
<button
|
||||
key={tab.key}
|
||||
type="button"
|
||||
className={activeTab === tab.key ? 'active' : ''}
|
||||
onClick={() => changeTab(tab.key)}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
{activeTab === 'o-firmie' && (
|
||||
<div className="company-profile-body">
|
||||
<div className="company-profile-main">
|
||||
<section className="settings-card company-about">
|
||||
<h2>O firmie</h2>
|
||||
<p>{profile.bio || 'Ta firma nie dodała jeszcze opisu swojej działalności.'}</p>
|
||||
<div className="company-stats">
|
||||
<div><Icon name="pin" /><strong>{cityCount}</strong><small>{cityCount === 1 ? 'miasto' : 'miast(a)'}</small></div>
|
||||
<div><Icon name="house" /><strong>{profile.listingsCount}</strong><small>ogłoszeń</small></div>
|
||||
<div><Icon name="tag" /><strong>{forSaleCount}</strong><small>na sprzedaż</small></div>
|
||||
<div><Icon name="key" /><strong>{forRentCount}</strong><small>na wynajem</small></div>
|
||||
</div>
|
||||
<div className="company-profile-badges">
|
||||
<span><Icon name="calendar" /> W serwisie od {memberSinceLabel}</span>
|
||||
{lastActiveLabel && <span><Icon name="clock" /> {lastActiveLabel}</span>}
|
||||
{profile.verified && <span><Icon name="shield" /> Konto zweryfikowane</span>}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="settings-card">
|
||||
<div className="settings-card-head company-listings-head">
|
||||
<h2>Najnowsze ogłoszenia</h2>
|
||||
{profile.listingsCount > 3 && (
|
||||
<button type="button" className="company-see-all" onClick={() => changeTab('ogloszenia')}>
|
||||
Zobacz wszystkie <Icon name="arrow" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{profile.listings.length === 0 ? (
|
||||
<p className="admin-empty">Ta firma nie ma obecnie opublikowanych ogłoszeń.</p>
|
||||
) : (
|
||||
<div className="public-listing-grid">
|
||||
{profile.listings.slice(0, 3).map(renderListingCard)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
{contactCard}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'ogloszenia' && (
|
||||
<section className="settings-card">
|
||||
<div className="settings-card-head">
|
||||
<h2>Ogłoszenia ({profile.listingsCount})</h2>
|
||||
</div>
|
||||
{profile.listings.length === 0 ? (
|
||||
<p className="admin-empty">Ta firma nie ma obecnie opublikowanych ogłoszeń.</p>
|
||||
) : (
|
||||
<div className="public-listing-grid">
|
||||
{profile.listings.map(renderListingCard)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{activeTab === 'kontakt' && (
|
||||
<div className="company-profile-body">
|
||||
<section className="settings-card company-about">
|
||||
<h2>Kontakt</h2>
|
||||
{hasContactData ? (
|
||||
<p>Skorzystaj z danych kontaktowych firmy lub napisz wiadomość z poziomu jej ogłoszenia.</p>
|
||||
) : (
|
||||
<p>Ta firma nie udostępniła publicznie danych kontaktowych. Aby się z nią skontaktować, otwórz jedno z jej ogłoszeń i użyj przycisku „Napisz wiadomość”.</p>
|
||||
)}
|
||||
<div className="company-profile-badges">
|
||||
<span><Icon name="calendar" /> W serwisie od {memberSinceLabel}</span>
|
||||
{lastActiveLabel && <span><Icon name="clock" /> {lastActiveLabel}</span>}
|
||||
</div>
|
||||
</section>
|
||||
{contactCard}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Profil innego uzytkownika (/profil/:id) - otwierany z rozmowy w wiadomosciach.
|
||||
* Pokazuje wylacznie dane jawne; kontakt odbywa sie przez wiadomosci, nie przez e-mail.
|
||||
@@ -24745,7 +25170,12 @@ function PublicProfilePage() {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{!loading && !error && profile && !profile.privateProfile && (
|
||||
{/* Konto firmowe dostaje rozbudowany profil (banner, dane firmy, statystyki, oferty). */}
|
||||
{!loading && !error && profile && !profile.privateProfile && profile.accountType === 'COMPANY' && (
|
||||
<CompanyProfile profile={profile} />
|
||||
)}
|
||||
|
||||
{!loading && !error && profile && !profile.privateProfile && profile.accountType !== 'COMPANY' && (
|
||||
<>
|
||||
<section className="public-profile-cover-card">
|
||||
{/* Zdjecie w tle ustawione przez wlasciciela; bez niego zostaje grafika serwisu. */}
|
||||
@@ -24760,7 +25190,7 @@ function PublicProfilePage() {
|
||||
<div className="public-profile-meta">
|
||||
{profile.nick && <span><Icon name="user" /> @{profile.nick}</span>}
|
||||
<span><Icon name="calendar" /> W serwisie od {memberSinceLabel}</span>
|
||||
<span><Icon name="house" /> {profile.accountType === 'COMPANY' ? 'Firma' : 'Osoba prywatna'}</span>
|
||||
<span><Icon name="house" /> Osoba prywatna</span>
|
||||
</div>
|
||||
{/* Opis "o mnie" z ustawien konta - pole opcjonalne, wiec pokazujemy tylko gdy wypelnione. */}
|
||||
{profile.bio && <p className="public-profile-bio">{profile.bio}</p>}
|
||||
@@ -24823,7 +25253,7 @@ function PublicProfilePage() {
|
||||
<h2>O użytkowniku</h2>
|
||||
<p>Ten użytkownik nie dodał jeszcze opisu swojego profilu.</p>
|
||||
<div className="public-profile-badges">
|
||||
<span><Icon name="user" /> {profile.accountType === 'COMPANY' ? 'Konto firmowe' : 'Konto prywatne'}</span>
|
||||
<span><Icon name="user" /> Konto prywatne</span>
|
||||
<span><Icon name="calendar" /> W serwisie od {memberSinceLabel}</span>
|
||||
{profile.verified && <span><Icon name="shield" /> Zweryfikowany użytkownik</span>}
|
||||
<span><Icon name="message" /> Kontakt przez wiadomości</span>
|
||||
@@ -27316,6 +27746,10 @@ function ListingDetailPage({
|
||||
: listing.ownerAccountType === 'PERSONAL'
|
||||
? 'Osoba prywatna'
|
||||
: 'Oficjalny kontakt zespołem serwisu Polska Lokalnie';
|
||||
// Dodatkowe wiersze na karcie sprzedajacego - realne dane konta wlasciciela (nie pokazujemy ich
|
||||
// przy wlasnym ogloszeniu ani dla wpisow bez konta w serwisie, gdzie oba pola sa puste).
|
||||
const sellerLastActiveText = isOwner ? '' : sellerLastActive(listing.ownerLastActiveAt);
|
||||
const sellerMemberSinceText = isOwner ? '' : sellerMemberSince(listing.ownerCreatedAt);
|
||||
const favorite = isFavorite(listing.id);
|
||||
const listingAlertEnabled = isListingAlertEnabled(listing.id);
|
||||
const virtualTourEmbed = listing.virtualTourUrl ? getVirtualTourEmbed(listing.virtualTourUrl) : null;
|
||||
@@ -27324,7 +27758,7 @@ function ListingDetailPage({
|
||||
const copyShareLink = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(shareUrl);
|
||||
setShareHint('Skopiowano link');
|
||||
setShareHint('Link został skopiowany');
|
||||
window.setTimeout(() => setShareHint(null), 1800);
|
||||
} catch {
|
||||
setShareHint('Nie udało się skopiować');
|
||||
@@ -27336,6 +27770,26 @@ function ListingDetailPage({
|
||||
window.open(url, '_blank', 'noopener,noreferrer');
|
||||
};
|
||||
|
||||
// Udostepnianie e-mailem: domyslny klient poczty z tytulem oferty w temacie i linkiem w tresci.
|
||||
const shareByEmail = () => {
|
||||
const subject = encodeURIComponent(listing.title);
|
||||
const body = encodeURIComponent(`${listing.title}\n${shareUrl}`);
|
||||
window.location.href = `mailto:?subject=${subject}&body=${body}`;
|
||||
};
|
||||
|
||||
// Systemowe udostepnianie (telefon) - a gdy niedostepne albo uzytkownik anuluje, kopiujemy link.
|
||||
const shareViaSystem = async () => {
|
||||
if (typeof navigator !== 'undefined' && typeof navigator.share === 'function') {
|
||||
try {
|
||||
await navigator.share({ title: listing.title, text: listing.city, url: shareUrl });
|
||||
} catch {
|
||||
// Uzytkownik zamknal panel udostepniania - nic nie robimy.
|
||||
}
|
||||
return;
|
||||
}
|
||||
await copyShareLink();
|
||||
};
|
||||
|
||||
// Przycisk glowny pokazuje numer telefonu (wiadomosci obsluguje osobny przycisk "Napisz wiadomosc").
|
||||
const revealContact = () => {
|
||||
setIsPhoneRevealed(true);
|
||||
@@ -27791,6 +28245,8 @@ function ListingDetailPage({
|
||||
<div>
|
||||
<strong>{sellerName}</strong>
|
||||
<small>{sellerRole}</small>
|
||||
{sellerLastActiveText && <small className="listing-detail-seller-meta">{sellerLastActiveText}</small>}
|
||||
{sellerMemberSinceText && <small className="listing-detail-seller-meta">{sellerMemberSinceText}</small>}
|
||||
{isPhoneRevealed && listing.contactPhone && (
|
||||
<a href={`tel:${listing.contactPhone.replace(/\s+/g, '')}`}><Icon name="phone" /> {listing.contactPhone}</a>
|
||||
)}
|
||||
@@ -27809,20 +28265,10 @@ function ListingDetailPage({
|
||||
<strong>Udostępnij ofertę</strong>
|
||||
<div className="listing-detail-share-actions">
|
||||
<button type="button" onClick={copyShareLink} aria-label="Kopiuj link"><Icon name="link" /></button>
|
||||
<button type="button" onClick={() => openShareWindow(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`)} aria-label="Udostępnij na Facebooku">f</button>
|
||||
<button type="button" onClick={() => openShareWindow(`https://m.me/?link=${encodeURIComponent(shareUrl)}`)} aria-label="Udostępnij w Messengerze"><Icon name="message" /></button>
|
||||
<button type="button" onClick={() => openShareWindow(`https://wa.me/?text=${encodeURIComponent(`${listing.title} ${shareUrl}`)}`)} aria-label="Udostępnij na WhatsApp">wa</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (navigator.share) {
|
||||
await navigator.share({ title: listing.title, text: listing.city, url: shareUrl });
|
||||
return;
|
||||
}
|
||||
await copyShareLink();
|
||||
}}
|
||||
aria-label="Więcej opcji"
|
||||
>
|
||||
<button type="button" onClick={() => openShareWindow(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`)} aria-label="Udostępnij na Facebooku"><Icon name="facebook" /></button>
|
||||
<button type="button" onClick={shareByEmail} aria-label="Udostępnij przez e-mail"><Icon name="message" /></button>
|
||||
<button type="button" onClick={() => openShareWindow(`https://wa.me/?text=${encodeURIComponent(`${listing.title} ${shareUrl}`)}`)} aria-label="Udostępnij na WhatsApp"><Icon name="whatsapp" /></button>
|
||||
<button type="button" onClick={shareViaSystem} aria-label="Więcej opcji">
|
||||
<Icon name="dots" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -28346,6 +28792,45 @@ function AdminListingPreviewModal({ listingId, busy, onClose, onApprove, onRejec
|
||||
|
||||
function Icon({ name }: { name: string }) {
|
||||
switch (name) {
|
||||
// Logotypy marek - wypelniane (fill), a nie obrysowane jak reszta ikon, wiec inline style
|
||||
// nadpisuje globalna regule `svg { fill:none; stroke:currentColor }`. Kolory oryginalne marek.
|
||||
case 'facebook':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" style={{ fill: '#1877F2', stroke: 'none' }}>
|
||||
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073Z" />
|
||||
</svg>
|
||||
);
|
||||
case 'whatsapp':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" style={{ fill: '#25D366', stroke: 'none' }}>
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.71.306 1.263.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.885-9.885 9.885M20.52 3.449C18.24 1.245 15.24 0 12.045 0 5.463 0 .104 5.359.101 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.335-1.652a11.882 11.882 0 005.71 1.446h.006c6.585 0 11.946-5.359 11.949-11.893a11.821 11.821 0 00-3.481-8.413Z" />
|
||||
</svg>
|
||||
);
|
||||
case 'linkedin':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" style={{ fill: 'currentColor', stroke: 'none' }}>
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
);
|
||||
case 'instagram':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" style={{ fill: 'currentColor', stroke: 'none' }}>
|
||||
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z" />
|
||||
</svg>
|
||||
);
|
||||
case 'youtube':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" style={{ fill: 'currentColor', stroke: 'none' }}>
|
||||
<path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
|
||||
</svg>
|
||||
);
|
||||
case 'globe':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M3 12h18M12 3c2.6 2.7 2.6 15.3 0 18M12 3c-2.6 2.7-2.6 15.3 0 18" />
|
||||
</svg>
|
||||
);
|
||||
case 'logo':
|
||||
return (
|
||||
<svg viewBox="0 0 28 28" aria-hidden="true">
|
||||
|
||||
@@ -21,6 +21,14 @@ export type UserSettings = {
|
||||
profileVisibility: ProfileVisibility;
|
||||
avatarImage: string | null;
|
||||
coverImage: string | null;
|
||||
// Dane firmowe na profilu publicznym (konto COMPANY).
|
||||
companyTagline: string | null;
|
||||
website: string | null;
|
||||
socialLinkedin: string | null;
|
||||
socialFacebook: string | null;
|
||||
socialInstagram: string | null;
|
||||
socialYoutube: string | null;
|
||||
publicContactVisible: boolean;
|
||||
currency: Currency;
|
||||
areaUnit: AreaUnit;
|
||||
directOffersOnly: boolean;
|
||||
|
||||
@@ -25960,6 +25960,13 @@ a.city-card:hover {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
/* Wiersze z aktywnoscia i data zalozenia konta sprzedajacego - drobniejsze niz podpis roli,
|
||||
z lekkim odstepem od "Firma"/"Osoba prywatna". */
|
||||
.listing-detail-seller-card .listing-detail-seller-meta {
|
||||
color: #8595ab;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.listing-detail-seller a {
|
||||
align-items: center;
|
||||
color: #129357;
|
||||
@@ -26072,6 +26079,8 @@ a.city-card:hover {
|
||||
color: #304760;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
/* Przyciski rozkladaja sie rowno na cala szerokosc sekcji - bez pustego miejsca po prawej. */
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
height: 30px;
|
||||
@@ -34629,3 +34638,446 @@ a.listing-detail-back {
|
||||
position: static;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
/* ===== Profil firmy (rozbudowany widok konta COMPANY) + pola firmowe w ustawieniach ===== */
|
||||
|
||||
/* Pole formularza rozciagniete na obie kolumny (haslo, www). */
|
||||
.settings-card .profile-form-grid .profile-form-wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* Firmowa karta ustawien: social media w dwoch kolumnach na szerszych ekranach. */
|
||||
.settings-card[aria-label="Profil firmy"] .profile-form-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.settings-toggle-row {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.settings-toggle-row input {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.settings-toggle-row span {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.settings-toggle-row strong {
|
||||
color: #1b2b44;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.settings-toggle-row small {
|
||||
color: #6f7f92;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.settings-card-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.settings-card-actions .primary {
|
||||
background: #12a764;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.settings-card-actions .primary:hover {
|
||||
background: #0f8f54;
|
||||
}
|
||||
|
||||
.settings-card-actions .primary:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* --- Naglowek profilu firmy: baner + logo + tozsamosc + social + zakladki --- */
|
||||
.company-profile {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.company-profile-header {
|
||||
background: #fff;
|
||||
border: 1px solid #e8edf4;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.company-profile-banner {
|
||||
background-color: #dce7f2;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
height: 190px;
|
||||
}
|
||||
|
||||
.company-profile-head-row {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding: 0 24px 18px;
|
||||
}
|
||||
|
||||
.company-profile-logo {
|
||||
background: #fff;
|
||||
border: 1px solid #e8edf4;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 24px rgba(20, 40, 70, 0.12);
|
||||
flex-shrink: 0;
|
||||
margin-top: -46px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.company-profile-logo-img {
|
||||
border-radius: 12px;
|
||||
height: 92px;
|
||||
width: 92px;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.company-profile-identity {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.company-profile-identity h1 {
|
||||
color: #12233d;
|
||||
font-size: 26px;
|
||||
margin: 2px 0 0;
|
||||
}
|
||||
|
||||
.company-verified-badge {
|
||||
align-items: center;
|
||||
background: #e7f7ee;
|
||||
border-radius: 999px;
|
||||
color: #0f8f54;
|
||||
display: inline-flex;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
gap: 5px;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.company-verified-badge svg {
|
||||
height: 13px;
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.company-profile-tagline {
|
||||
color: #5a6b82;
|
||||
font-size: 14px;
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
|
||||
.company-profile-quicklinks {
|
||||
color: #4a5c76;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 18px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.company-profile-quicklinks > span,
|
||||
.company-profile-quicklinks > a {
|
||||
align-items: center;
|
||||
color: #4a5c76;
|
||||
display: inline-flex;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
gap: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.company-profile-quicklinks a {
|
||||
color: #0f8f54;
|
||||
}
|
||||
|
||||
.company-profile-quicklinks svg {
|
||||
color: #12a764;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.company-phone-reveal {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #0f8f54;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.company-profile-socials {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.company-profile-socials a {
|
||||
align-items: center;
|
||||
background: #f2f6fb;
|
||||
border: 1px solid #e1e9f2;
|
||||
border-radius: 999px;
|
||||
color: #34495f;
|
||||
display: inline-flex;
|
||||
height: 34px;
|
||||
justify-content: center;
|
||||
width: 34px;
|
||||
}
|
||||
|
||||
.company-profile-socials a:hover {
|
||||
background: #e7eef6;
|
||||
color: #12233d;
|
||||
}
|
||||
|
||||
.company-profile-socials svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.company-profile-tabs {
|
||||
border-top: 1px solid #eef2f7;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.company-profile-tabs button {
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: #5a6b82;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
padding: 14px 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.company-profile-tabs button:hover {
|
||||
color: #12233d;
|
||||
}
|
||||
|
||||
.company-profile-tabs button.active {
|
||||
border-bottom-color: #12a764;
|
||||
color: #0f8f54;
|
||||
}
|
||||
|
||||
/* --- Tresc: dwie kolumny (glowna + karta kontaktu), zwijane na waskich ekranach --- */
|
||||
.company-profile-body {
|
||||
align-items: start;
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
grid-template-columns: minmax(0, 1fr) 320px;
|
||||
}
|
||||
|
||||
.company-profile-main {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.company-about p {
|
||||
color: #40506a;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.company-stats {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin: 16px 0 4px;
|
||||
}
|
||||
|
||||
.company-stats > div {
|
||||
background: #f6faf7;
|
||||
border: 1px solid #e4f0e8;
|
||||
border-radius: 12px;
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
padding: 14px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.company-stats svg {
|
||||
color: #12a764;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.company-stats strong {
|
||||
color: #12233d;
|
||||
font-size: 22px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.company-stats small {
|
||||
color: #6f7f92;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.company-profile-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.company-profile-badges span {
|
||||
align-items: center;
|
||||
background: #f2f6fb;
|
||||
border-radius: 999px;
|
||||
color: #46586f;
|
||||
display: inline-flex;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
gap: 6px;
|
||||
padding: 5px 11px;
|
||||
}
|
||||
|
||||
.company-profile-badges svg {
|
||||
color: #12a764;
|
||||
height: 13px;
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.company-listings-head {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.company-see-all {
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #0f8f54;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.company-see-all svg {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* --- Karta "Szybki kontakt" (aside) --- */
|
||||
.company-profile-contact-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e8edf4;
|
||||
border-radius: 14px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
padding: 18px;
|
||||
position: sticky;
|
||||
top: 92px;
|
||||
}
|
||||
|
||||
.company-profile-contact-card h2 {
|
||||
color: #12233d;
|
||||
font-size: 17px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.company-profile-contact-card p {
|
||||
color: #5a6b82;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.company-contact-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.company-contact-list li {
|
||||
align-items: center;
|
||||
color: #34495f;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.company-contact-list a {
|
||||
color: #0f8f54;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.company-contact-list svg {
|
||||
color: #12a764;
|
||||
flex-shrink: 0;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.company-contact-www {
|
||||
align-items: center;
|
||||
background: #12a764;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
padding: 11px 14px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.company-contact-www:hover {
|
||||
background: #0f8f54;
|
||||
}
|
||||
|
||||
.company-contact-www svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.company-contact-empty {
|
||||
color: #6f7f92;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.company-profile-body {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.company-profile-head-row {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.company-profile-socials {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.company-stats {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.company-profile-contact-card {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user