This commit is contained in:
Tomas Dvorak
2025-11-02 01:04:02 +01:00
parent ac886502e0
commit b9cea0cd77
153 changed files with 43713 additions and 1700 deletions
+201 -98
View File
@@ -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>
);
};