mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
dev day #79
This commit is contained in:
+201
-98
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useRef, useState, useMemo } from 'react';
|
||||
import React, { useEffect, useRef, useState, useMemo, Suspense } from 'react';
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import MainLayout from '../components/layout/MainLayout';
|
||||
import { FiArrowRight, FiCalendar, FiUsers, FiAward, FiChevronLeft, FiChevronRight } from 'react-icons/fi';
|
||||
import { FiArrowRight, FiCalendar, FiUsers, FiAward, FiChevronLeft, FiChevronRight, FiEdit } from 'react-icons/fi';
|
||||
import '../styles/theme.css';
|
||||
import '../styles/sparta-styles.css';
|
||||
import '../styles/club-styles.css';
|
||||
@@ -11,19 +12,20 @@ import { assetUrl, sanitizeClubName } from '../utils/url';
|
||||
import { getPlayers as apiGetPlayers, Player as ApiPlayer } from '../services/players';
|
||||
import { getSponsors as apiGetSponsors, Sponsor as ApiSponsor } from '../services/sponsors';
|
||||
import { getBanners as apiGetBanners, Banner as ApiBanner } from '../services/banners';
|
||||
import BannerDisplay from '../components/banners/BannerDisplay';
|
||||
import BlogCardsScroller from '../components/home/BlogCardsScroller';
|
||||
import BlogSwiper from '../components/home/BlogSwiper';
|
||||
import VideosSection from '../components/home/VideosSection';
|
||||
import MerchSection from '../components/home/MerchSection';
|
||||
import PollsWidget from '../components/home/PollsWidget';
|
||||
import GallerySection from '../components/home/GallerySection';
|
||||
import { translateNationality, getCountryFlag } from '../utils/nationality';
|
||||
const BannerDisplay = React.lazy(() => import('../components/banners/BannerDisplay'));
|
||||
const BlogCardsScroller = React.lazy(() => import('../components/home/BlogCardsScroller'));
|
||||
const BlogSwiper = React.lazy(() => import('../components/home/BlogSwiper'));
|
||||
const VideosSection = React.lazy(() => import('../components/home/VideosSection'));
|
||||
const MerchSection = React.lazy(() => import('../components/home/MerchSection'));
|
||||
const PollsWidget = React.lazy(() => import('../components/home/PollsWidget'));
|
||||
const GallerySection = React.lazy(() => import('../components/home/GallerySection'));
|
||||
import { getArticles as apiGetArticles, Article as ApiArticle } from '../services/articles';
|
||||
import { getCompetitionAliasesPublic, CompetitionAlias } from '../services/competitionAliases';
|
||||
import { getUpcomingEvents } from '../services/eventService';
|
||||
import NewsletterSubscribe from '../components/newsletter/NewsletterSubscribe';
|
||||
import MyUIbrixStyleEditor from '../components/editor/MyUIbrixEditor';
|
||||
import MyUIbrixErrorBoundary from '../components/editor/MyUIbrixErrorBoundary';
|
||||
const NewsletterSubscribe = React.lazy(() => import('../components/newsletter/NewsletterSubscribe'));
|
||||
const MyUIbrixStyleEditor = React.lazy(() => import('../components/editor/MyUIbrixEditor'));
|
||||
const MyUIbrixErrorBoundary = React.lazy(() => import('../components/editor/MyUIbrixErrorBoundary'));
|
||||
import ClubModal from '../components/home/ClubModal';
|
||||
import MatchModal from '../components/home/MatchModal';
|
||||
import { useAllPageElementConfigs } from '../hooks/usePageElementConfig';
|
||||
@@ -31,10 +33,11 @@ import { API_URL } from '../services/api';
|
||||
import { TeamLogo } from '../components/common/TeamLogo';
|
||||
import ClubHeroTopbar from '../components/home/ClubHeroTopbar';
|
||||
import NewsList from '../components/pack/NewsList';
|
||||
import StandingsCard from '../components/pack/StandingsCard';
|
||||
const StandingsCard = React.lazy(() => import('../components/pack/StandingsCard'));
|
||||
import NextMatch from '../components/pack/NextMatch';
|
||||
import MatchesSlider from '../components/pack/MatchesSlider';
|
||||
const MatchesSlider = React.lazy(() => import('../components/pack/MatchesSlider'));
|
||||
import ActivitiesList from '../components/pack/ActivitiesList';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
// Types for real API-driven data
|
||||
type NewsItem = {
|
||||
@@ -100,7 +103,7 @@ const HomePage: React.FC = () => {
|
||||
// Matches slider auto-centering handled internally by MatchesSlider component
|
||||
|
||||
// API-driven players and sponsors
|
||||
type UiPlayer = { id:number|string; name:string; number?:number; position?:string; image?:string; slug?:string; age?: number };
|
||||
type UiPlayer = { id:number|string; name:string; number?:number; position?:string; image?:string; slug?:string; age?: number; nationality?: string };
|
||||
type UiSponsor = { id:number|string; name:string; logo:string; url?:string; tier?: string };
|
||||
type UiBanner = { id:number|string; name:string; image:string; url?:string; placement?:string; width?:number; height?:number };
|
||||
type UiMerch = { id?: number|string; title?: string; image_url: string; url?: string };
|
||||
@@ -114,11 +117,14 @@ const HomePage: React.FC = () => {
|
||||
const [merchItems, setMerchItems] = useState<UiMerch[]>([]);
|
||||
const [merchEnabled, setMerchEnabled] = useState<boolean>(false);
|
||||
const [upcomingEvents, setUpcomingEvents] = useState<UiEvent[]>([]);
|
||||
const [defer, setDefer] = useState<boolean>(false);
|
||||
// Aliases
|
||||
const [aliases, setAliases] = useState<CompetitionAlias[]>([]);
|
||||
const [aliasMap, setAliasMap] = useState<Record<string, { alias: string; original_name?: string }>>({});
|
||||
const [settings, setSettings] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isEditingMode, setIsEditingMode] = useState<boolean>(false);
|
||||
const { user } = useAuth();
|
||||
|
||||
// MyUIbrix element configuration hook for live preview
|
||||
const { getVariant, isVisible, getStyles, loading: configLoading, refreshKey } = useAllPageElementConfigs('homepage');
|
||||
@@ -133,6 +139,18 @@ const HomePage: React.FC = () => {
|
||||
} catch {}
|
||||
}, [stylePack]);
|
||||
|
||||
useEffect(() => {
|
||||
const ric: any = (window as any).requestIdleCallback || ((cb: any) => setTimeout(cb, 1));
|
||||
ric(() => setDefer(true));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const has = typeof document !== 'undefined' && document.body.classList.contains('myuibrix-edit-mode');
|
||||
setIsEditingMode(!!has);
|
||||
} catch {}
|
||||
}, []);
|
||||
|
||||
const heroFallbackArticles = useMemo(() => featured.map((item, index) => ({
|
||||
id: typeof item.id === 'number' ? item.id : index,
|
||||
title: item.title,
|
||||
@@ -402,6 +420,7 @@ const HomePage: React.FC = () => {
|
||||
number: p.jersey_number,
|
||||
position: p.position,
|
||||
image: assetUrl(p.image_url) || undefined,
|
||||
nationality: (p as any).nationality,
|
||||
age: (function(iso?: string){
|
||||
if (!iso) return undefined;
|
||||
const d = new Date(iso);
|
||||
@@ -1343,7 +1362,7 @@ const HomePage: React.FC = () => {
|
||||
<div data-element="style-pack" data-variant={stylePack} style={{ display: 'none' }} />
|
||||
{/* Above-hero club bar (MyUIbrix managed) */}
|
||||
{isVisible('hero-topbar', true) && (
|
||||
<section data-element="hero-topbar" data-variant={getVariant('hero-topbar', 'minimal')} style={{ ...getStyles('hero-topbar') }}>
|
||||
<section key={`hero-topbar-${refreshKey}-${getVariant('hero-topbar', 'minimal')}`} data-element="hero-topbar" data-variant={getVariant('hero-topbar', 'minimal')} style={{ ...getStyles('hero-topbar') }}>
|
||||
<ClubHeroTopbar
|
||||
variant={(getVariant('hero-topbar', 'minimal') as any) as 'brand' | 'minimal' | 'badge'}
|
||||
fullBleed={getVariant('header', 'unified') === 'fullwidth'}
|
||||
@@ -1425,31 +1444,48 @@ const HomePage: React.FC = () => {
|
||||
|
||||
{/* Featured articles are now shown in the hero grid above, not here */}
|
||||
|
||||
{/* Sidebar banners (homepage_sidebar) */}
|
||||
{/* Sidebar banners (homepage_sidebar) - fixed edge rail, left/right via MyUIbrix variant */}
|
||||
{(banners || []).some(b => b.placement === 'homepage_sidebar') && (
|
||||
<section data-element="sidebar" data-variant={getVariant('sidebar', 'right')} className="banner banner-sidebar" style={{ margin: '24px 0', ...getStyles('sidebar') }}>
|
||||
{/* Simple responsive behavior: stack on mobile, sticky right rail on desktop */}
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<div style={{ width: 320, maxWidth: '100%', position: 'sticky' as const, top: 96 }}>
|
||||
{(banners || []).filter(b => b.placement === 'homepage_sidebar').map((b) => (
|
||||
<a key={b.id} href={b.url || '#'} target={b.url ? '_blank' : undefined} rel={b.url ? 'noopener noreferrer' : undefined} style={{ display: 'block', marginBottom: 12 }}>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<img loading="lazy" src={b.image} alt={b.name} style={{ width: b.width ? `${b.width}px` : '100%', height: b.height ? `${b.height}px` : 'auto', maxWidth: '100%' }} />
|
||||
</a>
|
||||
))}
|
||||
<section
|
||||
key={`sidebar-${refreshKey}-${getVariant('sidebar', 'right')}`}
|
||||
data-element="sidebar"
|
||||
data-variant={getVariant('sidebar', 'right')}
|
||||
className={`banner banner-sidebar sidebar-${getVariant('sidebar', 'right')}`}
|
||||
style={{
|
||||
// Use configured styles but force fixed rail placement
|
||||
...getStyles('sidebar'),
|
||||
position: 'fixed',
|
||||
top: 112,
|
||||
left: getVariant('sidebar', 'right') === 'left' ? 12 : 'auto',
|
||||
right: getVariant('sidebar', 'right') === 'left' ? 'auto' : 12,
|
||||
width: 320,
|
||||
maxWidth: '100%',
|
||||
zIndex: 50,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
{(banners || []).filter(b => b.placement === 'homepage_sidebar').map((b) => (
|
||||
<div key={b.id} className="card" style={{ display: 'block', marginBottom: 12, pointerEvents: 'auto', padding: 4 }}>
|
||||
<a href={b.url || '#'} target={b.url ? '_blank' : undefined} rel={b.url ? 'noopener noreferrer' : undefined} style={{ display: 'block' }}>
|
||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||
<img loading="lazy" src={b.image} alt={b.name} style={{ width: b.width ? `${b.width}px` : '100%', height: b.height ? `${b.height}px` : 'auto', maxWidth: '100%' }} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
)}
|
||||
{getVariant('hero', heroStyle) === 'scroller' && isVisible('hero', true) && (
|
||||
<section key={`hero-scroller-${refreshKey}`} data-element="hero" data-variant={getVariant('hero', heroStyle)} style={{ position: 'relative', ...getStyles('hero') }}>
|
||||
<BlogCardsScroller />
|
||||
<Suspense fallback={<div style={{ minHeight: 240 }} />}>
|
||||
<BlogCardsScroller />
|
||||
</Suspense>
|
||||
</section>
|
||||
)}
|
||||
{(getVariant('hero', heroStyle) === 'swiper' || getVariant('hero', heroStyle) === 'swiper_full') && isVisible('hero', true) && (
|
||||
<section key={`hero-swiper-${refreshKey}`} data-element="hero" data-variant={getVariant('hero', heroStyle)} style={getVariant('hero', heroStyle) === 'swiper_full' ? { position: 'relative', marginLeft: 'calc(50% - 50vw)', marginRight: 'calc(50% - 50vw)', ...getStyles('hero') } : { position: 'relative', ...getStyles('hero') }}>
|
||||
<BlogSwiper fallbackArticles={heroFallbackArticles}
|
||||
/>
|
||||
<Suspense fallback={<div style={{ minHeight: 280 }} />}>
|
||||
<BlogSwiper fallbackArticles={heroFallbackArticles} />
|
||||
</Suspense>
|
||||
</section>
|
||||
)}
|
||||
|
||||
@@ -1493,36 +1529,41 @@ const HomePage: React.FC = () => {
|
||||
);
|
||||
})()
|
||||
) : isVisible('matches', true) ? (
|
||||
<NextMatch
|
||||
data={{
|
||||
home: matches[0]?.homeTeam || clubName,
|
||||
home_logo_url: matches[0]?.homeLogoURL || clubLogo,
|
||||
away: matches[0]?.awayTeam || 'Soupeř',
|
||||
away_logo_url: matches[0]?.awayLogoURL,
|
||||
}}
|
||||
countdown={countdown}
|
||||
elementProps={{ 'data-element': 'matches', 'data-variant': getVariant('matches', 'compact'), style: { position: 'relative', ...getStyles('matches') } }}
|
||||
/>
|
||||
<div className="card">
|
||||
<NextMatch
|
||||
key={`matches-${refreshKey}-${getVariant('matches', 'compact')}`}
|
||||
data={{
|
||||
home: matches[0]?.homeTeam || clubName,
|
||||
home_logo_url: matches[0]?.homeLogoURL || clubLogo,
|
||||
away: matches[0]?.awayTeam || 'Soupeř',
|
||||
away_logo_url: matches[0]?.awayLogoURL,
|
||||
}}
|
||||
countdown={countdown}
|
||||
elementProps={{ 'data-element': 'matches', 'data-variant': getVariant('matches', 'compact'), style: { position: 'relative', ...getStyles('matches') } }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Full-bleed top banner (homepage_top) */}
|
||||
{(banners || []).some(b => b.placement === 'homepage_top') && (
|
||||
<BannerDisplay banners={banners as any} placement="homepage_top" />
|
||||
)}
|
||||
{/* (Removed) Full-bleed top banner (homepage_top) */}
|
||||
|
||||
{/* Matches slider with scores by competition (moved after news+tables) */}
|
||||
{facrCompetitions.length > 0 && (
|
||||
<MatchesSlider
|
||||
comps={facrCompetitions as any}
|
||||
activeIndex={matchesTab}
|
||||
onActiveChange={setMatchesTab}
|
||||
onMatchClick={(m: any, compName?: string) => {
|
||||
setSelectedMatch({ ...m, competition: compName, competitionName: compName });
|
||||
setIsMatchModalOpen(true);
|
||||
}}
|
||||
variant={getVariant('matches-slider', 'carousel') as any}
|
||||
elementProps={{ 'data-element': 'matches-slider', 'data-variant': getVariant('matches-slider', 'carousel'), style: { position: 'relative', ...getStyles('matches-slider') } }}
|
||||
/>
|
||||
defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<MatchesSlider
|
||||
key={`matches-slider-${refreshKey}-${getVariant('matches-slider', 'carousel')}`}
|
||||
comps={facrCompetitions as any}
|
||||
activeIndex={matchesTab}
|
||||
onActiveChange={setMatchesTab}
|
||||
onMatchClick={(m: any, compName?: string) => {
|
||||
setSelectedMatch({ ...m, competition: compName, competitionName: compName });
|
||||
setIsMatchModalOpen(true);
|
||||
}}
|
||||
variant={getVariant('matches-slider', 'carousel') as any}
|
||||
elementProps={{ 'data-element': 'matches-slider', 'data-variant': getVariant('matches-slider', 'carousel'), style: { position: 'relative', ...getStyles('matches-slider') } }}
|
||||
/>
|
||||
</Suspense>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{/* News + Tables: split into two independent sections */}
|
||||
@@ -1545,12 +1586,13 @@ const HomePage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<section
|
||||
key={`news-table-${refreshKey}-${newsVariant}-${getVariant('table', 'split_news')}`}
|
||||
className="standings"
|
||||
data-variant={variant}
|
||||
style={{ marginTop: 32 }}
|
||||
>
|
||||
{showNews && (
|
||||
<section data-element="news" data-variant={newsVariant} className="news-list" style={{ ...getStyles('news') }}>
|
||||
<section key={`news-${refreshKey}-${newsVariant}`} data-element="news" data-variant={newsVariant} className="news-list" style={{ ...getStyles('news') }}>
|
||||
<div className="section-head" style={{ marginTop: 0 }}>
|
||||
<h3>Další aktuality</h3>
|
||||
<a href="/news" className="see-all" style={{ fontSize: '0.85rem' }}>Zobrazit vše <FiArrowRight size={14} /></a>
|
||||
@@ -1564,46 +1606,55 @@ const HomePage: React.FC = () => {
|
||||
)}
|
||||
|
||||
{showTable && (
|
||||
<div data-element="table" data-variant={getVariant('table', 'split_news')} style={{ ...getStyles('table') }}>
|
||||
<div key={`table-${refreshKey}-${getVariant('table', 'split_news')}`} data-element="table" data-variant={getVariant('table', 'split_news')} style={{ ...getStyles('table') }}>
|
||||
<div className="section-head" style={{ marginTop: 0, marginBottom: 12 }}>
|
||||
<h3>Tabulky</h3>
|
||||
<a href="/tabulky" className="see-all" style={{ fontSize: '0.85rem' }}>Zobrazit vše <FiArrowRight size={14} /></a>
|
||||
</div>
|
||||
<StandingsCard
|
||||
variant={((): 'logos'|'plain' => { const v = getVariant('table_rows', 'logos'); return v === 'plain' ? 'plain' : 'logos'; })()}
|
||||
rows={(matchingStanding?.table || matchingStanding?.rows || []) as any}
|
||||
onRowClick={(row) => {
|
||||
const clubData = {
|
||||
team: (row as any).team?.name ?? (row as any).team ?? (row as any).club ?? '-',
|
||||
team_id: (row as any).team_id || '',
|
||||
team_logo_url: (row as any).team_logo_url,
|
||||
rank: (row as any).position ?? (row as any).pos ?? (row as any).rank ?? 0,
|
||||
played: (row as any).played ?? (row as any).matches ?? '-',
|
||||
wins: (row as any).wins ?? (row as any).win ?? '-',
|
||||
draws: (row as any).draws ?? (row as any).draw ?? '-',
|
||||
losses: (row as any).losses ?? (row as any).loss ?? '-',
|
||||
score: (row as any).score ?? '-',
|
||||
points: (row as any).points ?? (row as any).pts ?? '-',
|
||||
};
|
||||
setSelectedClub(clubData);
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<StandingsCard
|
||||
variant={((): 'logos'|'plain' => { const v = getVariant('table_rows', 'logos'); return v === 'plain' ? 'plain' : 'logos'; })()}
|
||||
rows={(matchingStanding?.table || matchingStanding?.rows || []) as any}
|
||||
onRowClick={(row) => {
|
||||
const clubData = {
|
||||
team: (row as any).team?.name ?? (row as any).team ?? (row as any).club ?? '-',
|
||||
team_id: (row as any).team_id || '',
|
||||
team_logo_url: (row as any).team_logo_url,
|
||||
rank: (row as any).position ?? (row as any).pos ?? (row as any).rank ?? 0,
|
||||
played: (row as any).played ?? (row as any).matches ?? '-',
|
||||
wins: (row as any).wins ?? (row as any).win ?? '-',
|
||||
draws: (row as any).draws ?? (row as any).draw ?? '-',
|
||||
losses: (row as any).losses ?? (row as any).loss ?? '-',
|
||||
score: (row as any).score ?? '-',
|
||||
points: (row as any).points ?? (row as any).pts ?? '-',
|
||||
};
|
||||
setSelectedClub(clubData);
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
</Suspense>
|
||||
) : null}
|
||||
{/* Banners under the table, inside the table column */}
|
||||
{(banners || []).some(b => b.placement === 'homepage_under_table') && (
|
||||
defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<BannerDisplay banners={banners as any} placement="homepage_under_table" />
|
||||
</Suspense>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Banner under tables (homepage_under_table) */}
|
||||
{(banners || []).some(b => b.placement === 'homepage_under_table') && (
|
||||
<BannerDisplay banners={banners as any} placement="homepage_under_table" />
|
||||
)}
|
||||
{/* (Moved) Banner under tables now renders inside the table column above */}
|
||||
|
||||
{/* Competition tables moved into right column below */}
|
||||
|
||||
{upcomingEvents.length > 0 && isVisible('activities', true) && (
|
||||
<section data-element="activities" data-variant={getVariant('activities', 'list')} style={{ marginTop: 32, marginBottom: 16, position: 'relative', ...getStyles('activities') }}>
|
||||
<section key={`activities-${refreshKey}-${getVariant('activities', 'list')}`} data-element="activities" data-variant={getVariant('activities', 'list')} style={{ marginTop: 32, marginBottom: 16, position: 'relative', ...getStyles('activities') }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 12px' }}>
|
||||
<div className="section-head" style={{ marginTop: 0 }}>
|
||||
<h3>Aktivity</h3>
|
||||
@@ -1616,17 +1667,18 @@ const HomePage: React.FC = () => {
|
||||
|
||||
{/* Players scroller */}
|
||||
{players.length > 0 && isVisible('team', false) && (
|
||||
<section data-element="team" data-variant={getVariant('team', 'grid')} className="players-scroller" style={{ marginTop: 32, position: 'relative', ...getStyles('team') }}>
|
||||
<section key={`team-${refreshKey}-${getVariant('team', 'grid')}`} data-element="team" data-variant={getVariant('team', 'grid')} className="players-scroller" style={{ marginTop: 32, position: 'relative', ...getStyles('team') }}>
|
||||
<div className="section-head">
|
||||
<h3>Hráči</h3>
|
||||
<a href="/players" className="see-all">Zobrazit vše <FiArrowRight /></a>
|
||||
</div>
|
||||
<div className="scroll-x">
|
||||
{players.map((p) => (
|
||||
<a key={p.id} href={p.slug ? `/players/${p.slug}` : `/players/${p.id}`} className="player-card">
|
||||
<a key={p.id} href={p.slug ? `/players/${p.slug}` : `/players/${p.id}`} className="player-card card">
|
||||
<div className="photo" style={{ backgroundImage: `url(${assetUrl(p.image) || p.image})` }} />
|
||||
<div className="meta">{typeof p.number !== 'undefined' ? (<><span className="nr">#{p.number}</span> {p.name}</>) : p.name}</div>
|
||||
<div className="pos">{p.position}</div>
|
||||
{p.nationality ? (<div className="nat"><span className="flag" style={{ marginRight: 6 }}>{getCountryFlag(p.nationality)}</span>{translateNationality(p.nationality)}</div>) : null}
|
||||
{typeof p.age === 'number' && <div className="age">{p.age} let</div>}
|
||||
</a>
|
||||
))}
|
||||
@@ -1636,35 +1688,56 @@ const HomePage: React.FC = () => {
|
||||
|
||||
{/* Gallery */}
|
||||
{isVisible('gallery', false) && (
|
||||
<section data-element="gallery" data-variant={getVariant('gallery', 'grid')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('gallery') }}>
|
||||
<section key={`gallery-${refreshKey}-${getVariant('gallery', 'grid')}`} data-element="gallery" data-variant={getVariant('gallery', 'grid')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('gallery') }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 12px' }}>
|
||||
<GallerySection zoneramaUrl={galleryUrl} />
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<GallerySection zoneramaUrl={galleryUrl} />
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Videos */}
|
||||
{isVisible('videos', false) && (
|
||||
<section data-element="videos" data-variant={getVariant('videos', 'grid')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('videos') }}>
|
||||
<section key={`videos-${refreshKey}-${getVariant('videos', 'carousel')}`} data-element="videos" data-variant={getVariant('videos', 'carousel')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('videos') }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 12px' }}>
|
||||
<VideosSection variant={(getVariant('videos', 'grid') as any) as 'grid' | 'carousel'} />
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<VideosSection
|
||||
key={`videos-comp-${refreshKey}-${getVariant('videos', 'carousel')}`}
|
||||
variant={(getVariant('videos', 'carousel') as any) as 'grid' | 'carousel'}
|
||||
/>
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{isVisible('merch', true) && (
|
||||
<section data-element="merch" data-variant={getVariant('merch', 'grid')} style={{ marginTop: 24, marginBottom: 24, position: 'relative', ...getStyles('merch') }}>
|
||||
<section key={`merch-${refreshKey}-${getVariant('merch', 'grid')}`} data-element="merch" data-variant={getVariant('merch', 'grid')} style={{ marginTop: 24, marginBottom: 24, position: 'relative', ...getStyles('merch') }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 12px' }}>
|
||||
<MerchSection />
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<MerchSection variant={(getVariant('merch', 'grid') as any) as 'grid' | 'carousel' | 'featured' | 'list'} />
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Polls / Voting */}
|
||||
{isVisible('poll', false) && (
|
||||
<section data-element="poll" data-variant={getVariant('poll', 'vertical')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('poll') }}>
|
||||
<section key={`poll-${refreshKey}-${getVariant('poll', 'vertical')}`} data-element="poll" data-variant={getVariant('poll', 'vertical')} style={{ marginTop: 32, marginBottom: 32, position: 'relative', ...getStyles('poll') }}>
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '0 12px' }}>
|
||||
<PollsWidget featuredOnly={true} maxPolls={1} title="Anketa" />
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<div className="card">
|
||||
<PollsWidget featuredOnly={true} maxPolls={1} title="Anketa" />
|
||||
</div>
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
@@ -1683,9 +1756,13 @@ const HomePage: React.FC = () => {
|
||||
|
||||
{/* CTA (Newsletter) moved up */}
|
||||
{isVisible('newsletter', false) && (
|
||||
<section data-element="newsletter" data-variant={getVariant('newsletter', 'default')} className="newsletter-cta" style={{ marginTop: 24, marginBottom: 24, position: 'relative', ...getStyles('newsletter') }}>
|
||||
<section key={`newsletter-${refreshKey}-${getVariant('newsletter', 'default')}`} data-element="newsletter" data-variant={getVariant('newsletter', 'default')} className="newsletter-cta" style={{ marginTop: 24, marginBottom: 24, position: 'relative', ...getStyles('newsletter') }}>
|
||||
<div className="card" style={{ maxWidth: 960, margin: '0 auto' }}>
|
||||
<NewsletterSubscribe />
|
||||
{defer ? (
|
||||
<Suspense fallback={null}>
|
||||
<NewsletterSubscribe />
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
@@ -1829,9 +1906,35 @@ const HomePage: React.FC = () => {
|
||||
console.log('Team clicked:', teamName);
|
||||
}}
|
||||
/>
|
||||
<MyUIbrixErrorBoundary>
|
||||
<MyUIbrixStyleEditor pageType="homepage" />
|
||||
</MyUIbrixErrorBoundary>
|
||||
{isEditingMode ? (
|
||||
<Suspense fallback={null}>
|
||||
<MyUIbrixErrorBoundary>
|
||||
<MyUIbrixStyleEditor pageType="homepage" />
|
||||
</MyUIbrixErrorBoundary>
|
||||
</Suspense>
|
||||
) : null}
|
||||
{user?.role === 'admin' && !isEditingMode ? (
|
||||
<div style={{ position: 'fixed', left: 16, bottom: 16, zIndex: 10000 }}>
|
||||
<Tooltip label="Aktivovat MyUIbrix Editor" placement="right">
|
||||
<IconButton
|
||||
aria-label="Upravit stránku"
|
||||
icon={<FiEdit />}
|
||||
colorScheme="blue"
|
||||
size="lg"
|
||||
borderRadius="full"
|
||||
onClick={() => {
|
||||
try {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('myuibrix', 'edit');
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
} catch {}
|
||||
try { document.body.classList.add('myuibrix-edit-mode'); } catch {}
|
||||
setIsEditingMode(true);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
</MainLayout>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user