Добавлено получение баннеров для главного слайдера с использованием GraphQL. Обновлен компонент HeroSlider для отображения активных баннеров с сортировкой. Реализована логика отображения дефолтного баннера при отсутствии данных. Обновлены стили и структура компонента для улучшения пользовательского интерфейса.

This commit is contained in:
Bivekich
2025-07-15 09:03:32 +03:00
parent 9c152501db
commit 3e98f8fed6
19 changed files with 1109 additions and 342 deletions

View File

@ -0,0 +1,209 @@
import React, { useState, useEffect } from 'react';
import { useQuery } from '@apollo/client';
import { GET_HERO_BANNERS } from '@/lib/graphql';
import Link from 'next/link';
interface HeroBanner {
id: string;
title: string;
subtitle?: string;
imageUrl: string;
linkUrl?: string;
isActive: boolean;
sortOrder: number;
}
const ProductOfDayBanner: React.FC = () => {
const [currentSlide, setCurrentSlide] = useState(0);
const { data, loading, error } = useQuery(GET_HERO_BANNERS, {
errorPolicy: 'all'
});
// Фильтруем только активные баннеры и сортируем их
const banners: HeroBanner[] = data?.heroBanners
?.filter((banner: HeroBanner) => banner.isActive)
?.slice()
?.sort((a: HeroBanner, b: HeroBanner) => a.sortOrder - b.sortOrder) || [];
// Если нет баннеров из админки, показываем дефолтный
const allBanners = banners.length > 0 ? banners : [{
id: 'default',
title: 'ДОСТАВИМ БЫСТРО!',
subtitle: 'Дополнительная скидка на товары с местного склада',
imageUrl: '/images/imgfb.png',
linkUrl: '',
isActive: true,
sortOrder: 0
}];
// Автопрокрутка слайдов
useEffect(() => {
if (allBanners.length > 1) {
const interval = setInterval(() => {
setCurrentSlide(prev => (prev + 1) % allBanners.length);
}, 5000); // Меняем слайд каждые 5 секунд
return () => clearInterval(interval);
}
}, [allBanners.length]);
// Сброс текущего слайда если он вне диапазона
useEffect(() => {
if (currentSlide >= allBanners.length) {
setCurrentSlide(0);
}
}, [allBanners.length, currentSlide]);
const handlePrevSlide = () => {
setCurrentSlide(prev => prev === 0 ? allBanners.length - 1 : prev - 1);
};
const handleNextSlide = () => {
setCurrentSlide(prev => (prev + 1) % allBanners.length);
};
const handleSlideIndicator = (index: number) => {
setCurrentSlide(index);
};
const currentBanner = allBanners[currentSlide];
const renderBannerContent = (banner: HeroBanner) => {
return (
<div className="div-block-128" style={{
backgroundImage: `url(${banner.imageUrl})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '200px',
width: '100%',
height: '100%',
borderRadius: '20px',
overflow: 'hidden'
}}>
{(banner.title || banner.subtitle) && (
<div style={{
position: 'absolute',
bottom: '20px',
left: '20px',
right: '20px',
color: 'white',
textShadow: '0 2px 4px rgba(0,0,0,0.5)'
}}>
{banner.title && (
<h3 style={{ margin: 0, fontSize: '18px', fontWeight: 'bold' }}>
{banner.title}
</h3>
)}
{banner.subtitle && (
<p style={{ margin: '5px 0 0 0', fontSize: '14px' }}>
{banner.subtitle}
</p>
)}
</div>
)}
</div>
);
};
const bannerContent = renderBannerContent(currentBanner);
const finalContent = currentBanner.linkUrl ? (
<Link href={currentBanner.linkUrl} style={{ cursor: 'pointer', display: 'block', width: '100%', height: '100%' }}>
{bannerContent}
</Link>
) : bannerContent;
return (
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
{finalContent}
{/* Навигация стрелками (показываем только если баннеров больше 1) */}
{allBanners.length > 1 && (
<>
<div
onClick={handlePrevSlide}
style={{
position: 'absolute',
left: '10px',
top: '50%',
transform: 'translateY(-50%)',
background: 'rgba(0,0,0,0.5)',
color: 'white',
border: 'none',
borderRadius: '50%',
width: '40px',
height: '40px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
zIndex: 10,
fontSize: '18px'
}}
>
</div>
<div
onClick={handleNextSlide}
style={{
position: 'absolute',
right: '10px',
top: '50%',
transform: 'translateY(-50%)',
background: 'rgba(0,0,0,0.5)',
color: 'white',
border: 'none',
borderRadius: '50%',
width: '40px',
height: '40px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
zIndex: 10,
fontSize: '18px'
}}
>
</div>
</>
)}
{/* Индикаторы слайдов (показываем только если баннеров больше 1) */}
{allBanners.length > 1 && (
<div style={{
position: 'absolute',
bottom: '10px',
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
gap: '8px',
zIndex: 10
}}>
{allBanners.map((_, index) => (
<div
key={index}
onClick={() => handleSlideIndicator(index)}
style={{
width: '10px',
height: '10px',
borderRadius: '50%',
background: index === currentSlide ? 'white' : 'rgba(255,255,255,0.5)',
cursor: 'pointer',
transition: 'background 0.3s'
}}
/>
))}
</div>
)}
</div>
);
};
export default ProductOfDayBanner;