mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
166 lines
5.1 KiB
TypeScript
166 lines
5.1 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { assetUrl } from '../../utils/url';
|
|
import { API_URL } from '../../services/api';
|
|
|
|
interface Sponsor {
|
|
id: number | string;
|
|
name: string;
|
|
logo: string;
|
|
url?: string;
|
|
tier?: string;
|
|
display_order?: number;
|
|
}
|
|
|
|
interface SponsorsSectionProps {
|
|
layout?: 'grid' | 'slider' | 'scroller' | 'pyramid';
|
|
theme?: 'dark' | 'light';
|
|
}
|
|
|
|
const resolveBackendUrl = (path: string) => {
|
|
try {
|
|
if (/^https?:\/\//i.test(path)) return path;
|
|
if (path.startsWith('/cache') || path.startsWith('/uploads') || path.startsWith('/api/')) {
|
|
const origin = new URL(API_URL, typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000').origin;
|
|
const abs = new URL(path, origin);
|
|
return abs.toString();
|
|
}
|
|
return path;
|
|
} catch {
|
|
return path;
|
|
}
|
|
};
|
|
|
|
const SponsorsSection: React.FC<SponsorsSectionProps> = ({
|
|
layout = 'grid',
|
|
theme = 'light'
|
|
}) => {
|
|
const [sponsors, setSponsors] = useState<Sponsor[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const fetchSponsors = async () => {
|
|
try {
|
|
// Try API first
|
|
const apiRes = await fetch(`${API_URL}/sponsors`);
|
|
if (apiRes.ok) {
|
|
const data = await apiRes.json();
|
|
if (!cancelled && Array.isArray(data)) {
|
|
const mapped = data.map((s: any) => ({
|
|
id: s.id,
|
|
name: s.name,
|
|
logo: assetUrl(s.logo_url) || '/images/sponsors/placeholder.png',
|
|
url: s.website_url || undefined,
|
|
tier: s.tier,
|
|
display_order: typeof s.display_order === 'number' ? s.display_order : undefined,
|
|
}));
|
|
setSponsors(mapped);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
}
|
|
} catch {}
|
|
|
|
// Fallback to cache
|
|
try {
|
|
const cacheRes = await fetch(resolveBackendUrl('/cache/prefetch/settings.json'), { cache: 'no-cache' });
|
|
if (cacheRes.ok) {
|
|
const settings = await cacheRes.json();
|
|
if (!cancelled) {
|
|
const sponsorsData = settings?.sponsors || settings?.partners || [];
|
|
if (Array.isArray(sponsorsData) && sponsorsData.length) {
|
|
setSponsors(
|
|
sponsorsData.map((s: any, i: number) => ({
|
|
id: s.id ?? i + 1,
|
|
name: s.name || 'Sponsor',
|
|
logo: assetUrl(s.logo_url || s.logoUrl || s.logo) || '/images/sponsors/placeholder.png',
|
|
url: s.url || s.website || s.link || '#',
|
|
tier: s.tier,
|
|
display_order: typeof s.display_order === 'number' ? s.display_order : undefined,
|
|
}))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} catch {}
|
|
|
|
if (!cancelled) {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchSponsors();
|
|
return () => { cancelled = true; };
|
|
}, []);
|
|
|
|
if (loading || sponsors.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const sorted = [...sponsors].sort((a: any, b: any) => {
|
|
const at = a.tier === 'general' ? 0 : 1;
|
|
const bt = b.tier === 'general' ? 0 : 1;
|
|
if (at !== bt) return at - bt;
|
|
const ao = (a as any).display_order ?? 9999;
|
|
const bo = (b as any).display_order ?? 9999;
|
|
if (ao !== bo) return ao - bo;
|
|
return String(a.name || '').localeCompare(String(b.name || ''));
|
|
});
|
|
const title = sorted.find((s: any) => s.tier === 'general') || sorted[0];
|
|
const others = sorted.filter((s) => s !== title);
|
|
|
|
return (
|
|
<section
|
|
className={`sponsors ${theme === 'dark' ? 'dark' : ''}`}
|
|
style={{
|
|
width: '100vw',
|
|
position: 'relative',
|
|
left: '50%',
|
|
right: '50%',
|
|
transform: 'translateX(-50%)',
|
|
paddingLeft: 'max(16px, calc((100vw - 1200px) / 2))',
|
|
paddingRight: 'max(16px, calc((100vw - 1200px) / 2))',
|
|
boxSizing: 'border-box',
|
|
marginTop: '32px',
|
|
marginBottom: '32px',
|
|
}}
|
|
>
|
|
<div className="section-head">
|
|
<h3>Sponzoři</h3>
|
|
</div>
|
|
{layout === 'grid' ? (
|
|
<>
|
|
{title && (
|
|
<div className="title-sponsor">
|
|
<a className="sponsor-tile" href={title.url || '#'} target="_blank" rel="noreferrer noopener">
|
|
<img src={title.logo} alt={title.name} />
|
|
</a>
|
|
</div>
|
|
)}
|
|
<div className="divider" aria-hidden />
|
|
<div className="sponsors-grid">
|
|
{others.map((s) => (
|
|
<a key={s.id} className="sponsor-tile" href={s.url || '#'} target="_blank" rel="noreferrer noopener">
|
|
<img src={s.logo} alt={s.name} />
|
|
</a>
|
|
))}
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="sponsors-slider">
|
|
<div className="track">
|
|
{[...sponsors, ...sponsors].map((s, idx) => (
|
|
<a key={`${s.id}-${idx}`} className="sponsor-tile" href={s.url || '#'} target="_blank" rel="noreferrer noopener">
|
|
<img src={s.logo} alt={s.name} />
|
|
</a>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default SponsorsSection;
|