Добавлено получение баннеров для главного слайдера с использованием GraphQL. Обновлен компонент HeroSlider для отображения активных баннеров с сортировкой. Реализована логика отображения дефолтного баннера при отсутствии данных. Обновлены стили и структура компонента для улучшения пользовательского интерфейса.
This commit is contained in:
209
src/components/index/ProductOfDayBanner.tsx
Normal file
209
src/components/index/ProductOfDayBanner.tsx
Normal 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;
|
Reference in New Issue
Block a user