This commit is contained in:
Emil Shanaty
2025-08-02 22:43:51 +03:00
parent 9eaf768b19
commit 612c4584ec
6 changed files with 119 additions and 51 deletions
View File
View File
View File
+106 -48
View File
@@ -1,64 +1,101 @@
const { useState, useEffect } = React;
// Demo data - РЕДАКТИРОВАТЬ ЗДЕСЬ ДЛЯ ДОБАВЛЕНИЯ НОВЫХ КАРТОЧЕК
const cardData = [
{
title: "Уютная беседка",
short: "Просторная беседка для отдыха на свежем воздухе",
images: [
'https://picsum.photos/600/400?random=2',
'https://picsum.photos/600/400?random=3',
'https://picsum.photos/600/400?random=4'
],
long: "Просторная деревянная беседка на 10-12 человек, оборудованная столами и скамейками. Идеальное место для семейных обедов, игр и вечерних посиделок. Беседка защищена от дождя и ветра, имеет хорошее освещение. Рядом расположена зона для барбекю и мангала. В беседке есть розетки для подключения электроприборов."
},
{
title: "Зона барбекю",
short: "Оборудованная площадка для приготовления шашлыков",
images: [
'https://picsum.photos/600/400?random=5',
'https://picsum.photos/600/400?random=6'
],
long: "Специально оборудованная зона для приготовления пищи на открытом огне. В наличии: стационарный мангал, решетки для гриля, столы для разделки продуктов. Рядом расположены мойка с проточной водой и место для хранения дров. Зона безопасна и соответствует всем противопожарным требованиям."
},
{
title: "Детская площадка",
short: "Безопасная игровая зона для детей разных возрастов",
images: [
'https://picsum.photos/600/400?random=7',
'https://picsum.photos/600/400?random=8',
'https://picsum.photos/600/400?random=9'
],
long: "Современная детская площадка с качелями, горками, песочницей и спортивными снарядами. Все оборудование изготовлено из экологически чистых материалов и регулярно проходит проверку безопасности. Площадка огорожена и имеет мягкое покрытие. Рядом установлены скамейки для родителей."
},
{
title: "Парковка",
short: "Удобная парковочная зона для автомобилей гостей",
images: [
'https://picsum.photos/600/400?random=10',
'https://picsum.photos/600/400?random=11'
],
long: "Просторная парковочная зона на 8-10 автомобилей. Парковка имеет твердое покрытие, хорошо освещается в темное время суток. Въезд и выезд удобные, есть место для разворота. Парковочные места размечены, территория охраняется. Рядом с парковкой расположена основная тропинка к зоне отдыха."
// Функция для получения настроек сайта из HTML
function getSiteConfigFromHTML() {
const configElement = document.querySelector('#site-config site-header');
if (!configElement) {
return {
bgImage: 'https://picsum.photos/1920/1080?random=1',
title: 'Станция Сосновая',
subtitle: 'База отдыха'
};
}
];
return {
bgImage: configElement.getAttribute('data-bg-image') || 'https://picsum.photos/1920/1080?random=1',
title: configElement.getAttribute('data-title') || 'Станция Сосновая',
subtitle: configElement.getAttribute('data-subtitle') || 'База отдыха'
};
}
// Функция для получения данных карточек из HTML
function getCardDataFromHTML() {
const cardElements = document.querySelectorAll('#cards-data card-data');
return Array.from(cardElements).map(element => {
const images = element.getAttribute('data-images');
return {
title: element.getAttribute('data-title'),
short: element.getAttribute('data-short'),
images: images ? images.split(',').map(img => img.trim()) : [],
long: element.getAttribute('data-long')
};
});
}
// Функция для установки фонового изображения
function setHeaderBackground(bgImage) {
useEffect(() => {
const headerBg = document.querySelector('.header-bg');
if (headerBg && bgImage) {
headerBg.style.backgroundImage = `url('${bgImage}')`;
}
}, [bgImage]);
}
function Header() {
const [siteConfig, setSiteConfig] = useState({
bgImage: '',
title: 'Станция Сосновая',
subtitle: 'База отдыха'
});
useEffect(() => {
const config = getSiteConfigFromHTML();
setSiteConfig(config);
}, []);
// Устанавливаем фоновое изображение
useEffect(() => {
const headerBg = document.querySelector('.header-bg');
if (headerBg && siteConfig.bgImage) {
headerBg.style.backgroundImage = `url('${siteConfig.bgImage}')`;
}
}, [siteConfig.bgImage]);
return (
<header className="header">
<div className="header-bg"></div>
<div className="header-overlay"></div>
<div className="header-content">
<h1>Станция Сосновая</h1>
<h2>База отдыха</h2>
<h1>{siteConfig.title}</h1>
<h2>{siteConfig.subtitle}</h2>
</div>
</header>
);
}
function SafeImage({ src, alt, className, fallback = '/images/placeholder.jpg' }) {
const [imgSrc, setImgSrc] = useState(src);
const handleError = () => {
console.warn(`Не удалось загрузить изображение: ${src}`);
setImgSrc(fallback);
};
return (
<img
src={imgSrc}
alt={alt}
className={className}
onError={handleError}
/>
);
}
function Card({ card, onCardClick }) {
return (
<div className="card" onClick={() => onCardClick(card)}>
<img
<SafeImage
src={card.images[0]}
alt={card.title}
className="card-image"
@@ -90,6 +127,7 @@ function Modal({ card, isOpen, onClose }) {
e.stopPropagation();
setCurrentImageIndex((i) => (i + 1) % card.images.length);
};
const prev = (e) => {
e.stopPropagation();
setCurrentImageIndex((i) => (i - 1 + card.images.length) % card.images.length);
@@ -103,7 +141,11 @@ function Modal({ card, isOpen, onClose }) {
<button className="close-button" onClick={onClose}>×</button>
</div>
<div className="carousel-container">
<img src={card.images[currentImageIndex]} className="carousel-image" alt={card.title} />
<SafeImage
src={card.images[currentImageIndex]}
className="carousel-image"
alt={card.title}
/>
{card.images.length > 1 && (
<>
<button className="carousel-button prev" onClick={prev}></button>
@@ -123,7 +165,15 @@ function Modal({ card, isOpen, onClose }) {
function CardsSection() {
const [selected, setSelected] = useState(null);
const [cardData, setCardData] = useState([]);
const isOpen = Boolean(selected);
// Загружаем данные карточек из HTML при монтировании компонента
useEffect(() => {
const data = getCardDataFromHTML();
setCardData(data);
}, []);
return (
<section className="cards-section">
<div className="container">
@@ -147,13 +197,21 @@ function Footer() {
);
const TelegramIcon = (
<svg className="contact-icon" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 0 0-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/></svg>
<svg className="contact-icon" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 0 0-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/>
</svg>
);
const WhatsAppIcon = (
<svg className="contact-icon" viewBox="0 0 24 24"><path d="M12.04 2c-5.46 0-9.91 4.45-9.91 9.91 0 1.75.46 3.45 1.32 4.95L2.05 22l5.25-1.38c1.45.79 3.08 1.21 4.74 1.21 5.46 0 9.91-4.45 9.91-9.91S17.5 2 12.04 2zm4.52 7.01c.2-.07.4-.02.4.24 0 .41-.12 2.28-.17 2.64-.05.36-.3.42-.6.26-.32-.18-1.37-.6-1.37-.6s-.72-.45-.94-.77c-.24-.32-.39-.66-.44-.87-.04-.2.07-.35.18-.46.1-.1.23-.15.23-.15s.18-.12.81.19c.47.23 1.17.8 1.17.8s.72.35.73.46c.01.11-.07.24-.07.24z"/></svg>
<svg className="contact-icon" viewBox="0 0 24 24">
<path d="M12.04 2c-5.46 0-9.91 4.45-9.91 9.91 0 1.75.46 3.45 1.32 4.95L2.05 22l5.25-1.38c1.45.79 3.08 1.21 4.74 1.21 5.46 0 9.91-4.45 9.91-9.91S17.5 2 12.04 2zm4.52 7.01c.2-.07.4-.02.4.24 0 .41-.12 2.28-.17 2.64-.05.36-.3.42-.6.26-.32-.18-1.37-.6-1.37-.6s-.72-.45-.94-.77c-.24-.32-.39-.66-.44-.87-.04-.2.07-.35.18-.46.1-.1.23-.15.23-.15s.18-.12.81.19c.47.23 1.17.8 1.17.8s.72.35.73.46c.01.11-.07.24-.07.24z"/>
</svg>
);
const PhoneIcon = (
<svg className="contact-icon" viewBox="0 0 24 24"><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>
<svg className="contact-icon" viewBox="0 0 24 24">
<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/>
</svg>
);
return (
@@ -185,4 +243,4 @@ function App() {
);
}
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(<App />, document.getElementById('root'));
+12 -2
View File
@@ -15,13 +15,23 @@
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<body>
<!-- Настройки сайта - РЕДАКТИРОВАТЬ ЗДЕСЬ -->
<div id="site-config" style="display: none;">
<site-header
data-bg-image="Images/cards/card1/20230928_224522.jpg"
data-title="Станция Сосновая"
data-subtitle="База отдыха">
</site-header>
</div>
<!-- Данные карточек - РЕДАКТИРОВАТЬ ЗДЕСЬ ДЛЯ ДОБАВЛЕНИЯ НОВЫХ КАРТОЧЕК -->
<div id="cards-data" style="display: none;">
<card-data
data-title="Уютная беседка"
data-short="Просторная беседка для отдыха на свежем воздухе"
data-main-image="/images/besedka/besedka1.jpg"
data-images="/images/besedka/besedka1.jpg,/images/besedka/besedka2.jpg,/images/besedka/besedka3.jpg"
data-main-image="Images/cards/card1/20230928_224522.jpg"
data-images="Images/cards/card1/20230928_224522.jpg,Images/cards/card1/360_0725_Stitch_XHC.jpg,Images/cards/20230928_224522.jpg"
data-long="Просторная деревянная беседка на 10-12 человек, оборудованная столами и скамейками. Идеальное место для семейных обедов, игр и вечерних посиделок. Беседка защищена от дождя и ветра, имеет хорошее освещение. Рядом расположена зона для барбекю и мангала. В беседке есть розетки для подключения электроприборов.">
</card-data>
+1 -1
View File
@@ -776,12 +776,12 @@ body {
left: 0;
width: 100%;
height: 100%;
background-image: url('https://picsum.photos/1920/1080?random=1');
background-size: cover;
background-position: center;
z-index: 1;
}
.header-overlay {
position: absolute;
top: 0;