Implement modern media gallery with enhanced features
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
- Fix CSS loading issue in project_detail.html template - Add comprehensive ModernMediaGallery JavaScript class with touch navigation - Implement glassmorphism design with backdrop-filter effects - Add responsive breakpoint system for mobile devices - Include embedded critical CSS styles for gallery functionality - Add technology sidebar with vertical list layout and hover effects - Support for images, videos, and embedded content with thumbnails - Add lightbox integration and media type badges - Implement progress bar and thumbnail navigation - Add keyboard controls (arrow keys) and touch swipe gestures - Include supplementary styles for video/embed placeholders - Fix template block naming compatibility (extra_css → extra_styles)
This commit is contained in:
192
create_test_projects.py
Normal file
192
create_test_projects.py
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
# Настройка Django
|
||||||
|
sys.path.append('/app/smartsoltech')
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartsoltech.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from web.models import Project, Category, Client, Service, Order
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
def create_test_projects():
|
||||||
|
# Создаем или получаем категории
|
||||||
|
categories = []
|
||||||
|
|
||||||
|
cat_web, _ = Category.objects.get_or_create(
|
||||||
|
slug='web-development',
|
||||||
|
defaults={
|
||||||
|
'name': 'Веб-разработка',
|
||||||
|
'icon': 'fas fa-code',
|
||||||
|
'order': 1,
|
||||||
|
'description': 'Создание веб-сайтов и приложений'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
categories.append(cat_web)
|
||||||
|
|
||||||
|
cat_mobile, _ = Category.objects.get_or_create(
|
||||||
|
slug='mobile-apps',
|
||||||
|
defaults={
|
||||||
|
'name': 'Мобильные приложения',
|
||||||
|
'icon': 'fas fa-mobile-alt',
|
||||||
|
'order': 2,
|
||||||
|
'description': 'Разработка iOS и Android приложений'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
categories.append(cat_mobile)
|
||||||
|
|
||||||
|
cat_design, _ = Category.objects.get_or_create(
|
||||||
|
slug='design',
|
||||||
|
defaults={
|
||||||
|
'name': 'Дизайн',
|
||||||
|
'icon': 'fas fa-palette',
|
||||||
|
'order': 3,
|
||||||
|
'description': 'UI/UX дизайн и брендинг'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
categories.append(cat_design)
|
||||||
|
|
||||||
|
cat_analytics, _ = Category.objects.get_or_create(
|
||||||
|
slug='analytics',
|
||||||
|
defaults={
|
||||||
|
'name': 'Аналитика',
|
||||||
|
'icon': 'fas fa-chart-bar',
|
||||||
|
'order': 4,
|
||||||
|
'description': 'Системы аналитики и отчетности'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
categories.append(cat_analytics)
|
||||||
|
|
||||||
|
cat_ecommerce, _ = Category.objects.get_or_create(
|
||||||
|
slug='ecommerce',
|
||||||
|
defaults={
|
||||||
|
'name': 'E-commerce',
|
||||||
|
'icon': 'fas fa-shopping-cart',
|
||||||
|
'order': 5,
|
||||||
|
'description': 'Интернет-магазины и торговые платформы'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
categories.append(cat_ecommerce)
|
||||||
|
|
||||||
|
# Создаем или получаем тестового клиента
|
||||||
|
client, _ = Client.objects.get_or_create(
|
||||||
|
email='test@example.com',
|
||||||
|
defaults={
|
||||||
|
'first_name': 'Тестовый',
|
||||||
|
'last_name': 'Клиент',
|
||||||
|
'phone_number': '+7-900-000-0000'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем или получаем тестовую услугу
|
||||||
|
service, _ = Service.objects.get_or_create(
|
||||||
|
name='Разработка сайта',
|
||||||
|
defaults={
|
||||||
|
'description': 'Профессиональная разработка веб-сайтов',
|
||||||
|
'price': 100000.00,
|
||||||
|
'category': cat_web
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Тестовые данные проектов
|
||||||
|
test_projects = [
|
||||||
|
{
|
||||||
|
'name': 'Корпоративный портал TechCorp',
|
||||||
|
'short_description': 'Современный корпоративный портал с системой управления документами, интеграцией с CRM и модулем HR.',
|
||||||
|
'description': '<p>Разработан комплексный корпоративный портал для компании TechCorp, включающий в себя систему управления документами, интеграцию с CRM-системой и модуль управления персоналом.</p><p>Основные функции: документооборот, календарь событий, внутренние новости, система заявок, интеграция с почтовыми сервисами.</p>',
|
||||||
|
'technologies': 'Django, PostgreSQL, Redis, Celery, Docker, React.js',
|
||||||
|
'duration': '4 месяца',
|
||||||
|
'team_size': 5,
|
||||||
|
'views_count': 1245,
|
||||||
|
'likes_count': 89,
|
||||||
|
'completion_date': date(2024, 8, 15),
|
||||||
|
'categories': [cat_web, cat_analytics],
|
||||||
|
'is_featured': True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Мобильное приложение FoodDelivery',
|
||||||
|
'short_description': 'Cross-platform приложение для доставки еды с геолокацией, онлайн-платежами и системой рейтингов.',
|
||||||
|
'description': '<p>Создано мобильное приложение для службы доставки еды с поддержкой iOS и Android платформ.</p><p>Функционал включает: поиск ресторанов по геолокации, онлайн-заказы, интеграцию с платежными системами, отслеживание курьера в реальном времени, система рейтингов и отзывов.</p>',
|
||||||
|
'technologies': 'React Native, Node.js, MongoDB, Socket.io, Stripe API',
|
||||||
|
'duration': '6 месяцев',
|
||||||
|
'team_size': 4,
|
||||||
|
'views_count': 892,
|
||||||
|
'likes_count': 156,
|
||||||
|
'completion_date': date(2024, 10, 20),
|
||||||
|
'categories': [cat_mobile, cat_ecommerce],
|
||||||
|
'is_featured': False
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Аналитическая панель SmartMetrics',
|
||||||
|
'short_description': 'Интерактивная панель управления с визуализацией данных, машинным обучением и предиктивной аналитикой.',
|
||||||
|
'description': '<p>Разработана комплексная система аналитики для обработки больших данных с возможностями машинного обучения.</p><p>Включает: интерактивные дашборды, автоматизированные отчеты, прогнозирование трендов, интеграция с различными источниками данных, алгоритмы машинного обучения.</p>',
|
||||||
|
'technologies': 'Python, Django, PostgreSQL, Redis, TensorFlow, D3.js, Pandas',
|
||||||
|
'duration': '5 месяцев',
|
||||||
|
'team_size': 6,
|
||||||
|
'views_count': 673,
|
||||||
|
'likes_count': 124,
|
||||||
|
'completion_date': date(2024, 7, 10),
|
||||||
|
'categories': [cat_analytics, cat_web],
|
||||||
|
'is_featured': True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'E-commerce платформа ShopMaster',
|
||||||
|
'short_description': 'Полнофункциональная платформа интернет-торговли с многопользовательскими магазинами и системой управления.',
|
||||||
|
'description': '<p>Создана масштабируемая e-commerce платформа, поддерживающая множественные магазины на одной основе.</p><p>Возможности: многопользовательская архитектура, система платежей, управление складом, программы лояльности, мобильная оптимизация, SEO инструменты.</p>',
|
||||||
|
'technologies': 'Laravel, MySQL, Redis, Elasticsearch, Vue.js, Stripe, PayPal',
|
||||||
|
'duration': '8 месяцев',
|
||||||
|
'team_size': 7,
|
||||||
|
'views_count': 1567,
|
||||||
|
'likes_count': 203,
|
||||||
|
'completion_date': date(2024, 11, 5),
|
||||||
|
'categories': [cat_ecommerce, cat_web, cat_mobile],
|
||||||
|
'is_featured': True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Дизайн-система BrandKit',
|
||||||
|
'short_description': 'Комплексная дизайн-система для финтех стартапа с фирменным стилем, UI-компонентами и брендбуком.',
|
||||||
|
'description': '<p>Разработана полная дизайн-система для финтех компании, включающая создание фирменного стиля, UI-компонентов и подробного брендбука.</p><p>Результат: логотип и фирменный стиль, библиотека UI-компонентов, руководство по использованию бренда, адаптация для различных платформ.</p>',
|
||||||
|
'technologies': 'Figma, Adobe Creative Suite, Principle, Sketch, InVision',
|
||||||
|
'duration': '3 месяца',
|
||||||
|
'team_size': 3,
|
||||||
|
'views_count': 445,
|
||||||
|
'likes_count': 78,
|
||||||
|
'completion_date': date(2024, 9, 30),
|
||||||
|
'categories': [cat_design],
|
||||||
|
'is_featured': False
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"Текущее количество проектов: {Project.objects.count()}")
|
||||||
|
|
||||||
|
# Создаем проекты
|
||||||
|
for i, project_data in enumerate(test_projects):
|
||||||
|
categories_to_add = project_data.pop('categories')
|
||||||
|
|
||||||
|
project, created = Project.objects.get_or_create(
|
||||||
|
name=project_data['name'],
|
||||||
|
defaults={
|
||||||
|
**project_data,
|
||||||
|
'client': client,
|
||||||
|
'service': service,
|
||||||
|
'status': 'completed',
|
||||||
|
'display_order': i + 1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
# Добавляем категории
|
||||||
|
project.categories.set(categories_to_add)
|
||||||
|
print(f"✅ Создан проект: {project.name}")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Проект уже существует: {project.name}")
|
||||||
|
|
||||||
|
print(f"\nИтого проектов в базе: {Project.objects.count()}")
|
||||||
|
print(f"Завершенных проектов: {Project.objects.filter(status='completed').count()}")
|
||||||
|
print(f"Избранных проектов: {Project.objects.filter(is_featured=True).count()}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
create_test_projects()
|
||||||
350
preview.html
Normal file
350
preview.html
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Проект Django E-commerce - Предварительный просмотр</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/css/lightbox.min.css">
|
||||||
|
<link rel="stylesheet" href="smartsoltech/static/assets/css/modern-styles.css">
|
||||||
|
<link rel="stylesheet" href="smartsoltech/static/assets/css/compact-gallery.css">"
|
||||||
|
<style>
|
||||||
|
.main-content {
|
||||||
|
padding-top: 2rem;
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
.content-wrapper {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
|
||||||
|
<!-- Компактная медиа-галерея -->
|
||||||
|
<div class="compact-media-gallery">
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- Основное изображение -->
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="main-media-display">
|
||||||
|
<div class="main-media-item" id="main-media">
|
||||||
|
<a href="https://via.placeholder.com/800x500/4f46e5/ffffff?text=Главное+изображение" data-lightbox="project-gallery" data-title="Главное изображение проекта">
|
||||||
|
<img src="https://via.placeholder.com/800x500/4f46e5/ffffff?text=Главное+изображение" alt="Главное изображение" class="main-media-img">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Сетка превью -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="media-thumbnails-grid">
|
||||||
|
<div class="thumbnail-item active" data-index="0">
|
||||||
|
<div class="thumbnail-wrapper" onclick="switchMainMedia('https://via.placeholder.com/800x500/4f46e5/ffffff?text=Главное+изображение', 'image', 'Главное изображение проекта')">
|
||||||
|
<img src="https://via.placeholder.com/200x200/4f46e5/ffffff?text=Превью+1" alt="Превью 1" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<i class="fas fa-search-plus"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://via.placeholder.com/800x500/4f46e5/ffffff?text=Главное+изображение" data-lightbox="project-gallery" data-title="Главное изображение проекта" style="display: none;"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="1">
|
||||||
|
<div class="thumbnail-wrapper" onclick="switchMainMedia('https://via.placeholder.com/800x500/7c3aed/ffffff?text=Скриншот+1', 'image', 'Скриншот интерфейса')">
|
||||||
|
<img src="https://via.placeholder.com/200x200/7c3aed/ffffff?text=Превью+2" alt="Превью 2" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<i class="fas fa-search-plus"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://via.placeholder.com/800x500/7c3aed/ffffff?text=Скриншот+1" data-lightbox="project-gallery" data-title="Скриншот интерфейса" style="display: none;"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="2">
|
||||||
|
<div class="thumbnail-wrapper" onclick="switchMainMedia('https://via.placeholder.com/800x500/f59e0b/ffffff?text=Скриншот+2', 'image', 'Мобильная версия')">
|
||||||
|
<img src="https://via.placeholder.com/200x200/f59e0b/ffffff?text=Превью+3" alt="Превью 3" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<i class="fas fa-search-plus"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://via.placeholder.com/800x500/f59e0b/ffffff?text=Скриншот+2" data-lightbox="project-gallery" data-title="Мобильная версия" style="display: none;"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="3">
|
||||||
|
<div class="thumbnail-wrapper" onclick="switchMainMedia('https://via.placeholder.com/800x500/10b981/ffffff?text=Админ+панель', 'image', 'Административная панель')">
|
||||||
|
<img src="https://via.placeholder.com/200x200/10b981/ffffff?text=Превью+4" alt="Превью 4" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<i class="fas fa-search-plus"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://via.placeholder.com/800x500/10b981/ffffff?text=Админ+панель" data-lightbox="project-gallery" data-title="Административная панель" style="display: none;"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Основной контент - двухколоночная структура -->
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Левая колонка - описание проекта -->
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="project-content">
|
||||||
|
<h2 class="mb-4">Описание проекта</h2>
|
||||||
|
<div class="description-text">
|
||||||
|
<p>Этот проект представляет собой <strong>современное веб-приложение</strong> электронной коммерции, разработанное с использованием Django и современных технологий фронтенда.</p>
|
||||||
|
|
||||||
|
<h3>Ключевые особенности</h3>
|
||||||
|
<ul>
|
||||||
|
<li><em>Адаптивный дизайн</em>, оптимизированный для всех устройств</li>
|
||||||
|
<li>Интеграция с <a href="#">популярными платежными системами</a></li>
|
||||||
|
<li>Многоуровневая система категорий товаров</li>
|
||||||
|
<li>Расширенная система поиска и фильтрации</li>
|
||||||
|
<li>Административная панель для управления контентом</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
Проект демонстрирует современные подходы к веб-разработке, включая использование микросервисной архитектуры, контейнеризации и непрерывной интеграции.
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<h4>Технические детали</h4>
|
||||||
|
<p>Для обеспечения высокой производительности использовались следующие решения:</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li><code>Redis</code> для кеширования данных</li>
|
||||||
|
<li><code>PostgreSQL</code> как основная база данных</li>
|
||||||
|
<li><code>Docker</code> для контейнеризации</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p><strong>Результат:</strong> Платформа способна обрабатывать <em>более 10,000 одновременных пользователей</em> с временем отклика менее 200ms.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Правая колонка - технологии -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="tech-sidebar-section">
|
||||||
|
<h3 class="tech-sidebar-title">Технологии</h3>
|
||||||
|
<div class="technologies-html-content">
|
||||||
|
<p><code>Python</code> <code>Django</code> <code>PostgreSQL</code></p>
|
||||||
|
<p><code>JavaScript</code> <code>HTML5</code> <code>CSS3</code></p>
|
||||||
|
<p><code>Docker</code> <code>Redis</code> <code>Bootstrap</code></p>
|
||||||
|
<p><strong>Дополнительно:</strong> <code>Bash</code> <code>SQLite3</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Дополнительная секция -->
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="additional-info p-4 rounded-4" style="background: #f8fafc; border: 1px solid #e2e8f0;">
|
||||||
|
<h3 class="mb-3">Результаты проекта</h3>
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-3 text-center">
|
||||||
|
<div class="stat-number" style="font-size: 2rem; font-weight: bold; color: #4f46e5;">150%</div>
|
||||||
|
<div class="stat-label">Рост продаж</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 text-center">
|
||||||
|
<div class="stat-number" style="font-size: 2rem; font-weight: bold; color: #4f46e5;">2.5x</div>
|
||||||
|
<div class="stat-label">Быстрее загрузка</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 text-center">
|
||||||
|
<div class="stat-number" style="font-size: 2rem; font-weight: bold; color: #4f46e5;">95%</div>
|
||||||
|
<div class="stat-label">Доступность</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 text-center">
|
||||||
|
<div class="stat-number" style="font-size: 2rem; font-weight: bold; color: #4f46e5;">100%</div>
|
||||||
|
<div class="stat-label">Адаптивность</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Карусель похожих проектов -->
|
||||||
|
<div class="similar-projects-section">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Похожие проекты</h2>
|
||||||
|
<div class="similar-projects-carousel">
|
||||||
|
<div class="swiper similarSwiper">
|
||||||
|
<div class="swiper-wrapper">
|
||||||
|
<div class="swiper-slide">
|
||||||
|
<div class="similar-project-card">
|
||||||
|
<div class="similar-thumb">
|
||||||
|
<img src="https://via.placeholder.com/300x200/4f46e5/ffffff?text=Проект+1" alt="Проект 1">
|
||||||
|
<div class="project-overlay">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="similar-content">
|
||||||
|
<h4 class="project-title">E-commerce платформа</h4>
|
||||||
|
<p class="project-description">Современная платформа для онлайн-торговли с интеграцией платежных систем</p>
|
||||||
|
<div class="project-categories">
|
||||||
|
<span class="category-tag">Web</span>
|
||||||
|
<span class="category-tag">Django</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="swiper-slide">
|
||||||
|
<div class="similar-project-card">
|
||||||
|
<div class="similar-thumb">
|
||||||
|
<img src="https://via.placeholder.com/300x200/7c3aed/ffffff?text=Проект+2" alt="Проект 2">
|
||||||
|
<div class="project-overlay">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="similar-content">
|
||||||
|
<h4 class="project-title">CRM система</h4>
|
||||||
|
<p class="project-description">Система управления клиентскими отношениями с аналитикой</p>
|
||||||
|
<div class="project-categories">
|
||||||
|
<span class="category-tag">CRM</span>
|
||||||
|
<span class="category-tag">Analytics</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="swiper-slide">
|
||||||
|
<div class="similar-project-card">
|
||||||
|
<div class="similar-thumb">
|
||||||
|
<img src="https://via.placeholder.com/300x200/f59e0b/ffffff?text=Проект+3" alt="Проект 3">
|
||||||
|
<div class="project-overlay">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="similar-content">
|
||||||
|
<h4 class="project-title">Мобильное приложение</h4>
|
||||||
|
<p class="project-description">iOS и Android приложение для управления задачами</p>
|
||||||
|
<div class="project-categories">
|
||||||
|
<span class="category-tag">Mobile</span>
|
||||||
|
<span class="category-tag">React Native</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="swiper-slide">
|
||||||
|
<div class="similar-project-card">
|
||||||
|
<div class="similar-thumb">
|
||||||
|
<div class="image-placeholder">
|
||||||
|
<div class="placeholder-content">
|
||||||
|
<i class="fas fa-image placeholder-icon"></i>
|
||||||
|
<div class="placeholder-text">Проект без изображения</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="project-overlay">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="similar-content">
|
||||||
|
<h4 class="project-title">Analytics Dashboard</h4>
|
||||||
|
<p class="project-description">Интерактивная панель аналитики с визуализацией данных</p>
|
||||||
|
<div class="project-categories">
|
||||||
|
<span class="category-tag">Analytics</span>
|
||||||
|
<span class="category-tag">D3.js</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="swiper-button-next similar-next"></div>
|
||||||
|
<div class="swiper-button-prev similar-prev"></div>
|
||||||
|
<div class="swiper-pagination similar-pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/js/lightbox.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Функция переключения основного медиа
|
||||||
|
function switchMainMedia(url, type, caption, poster = '') {
|
||||||
|
const mainMediaContainer = document.getElementById('main-media');
|
||||||
|
if (!mainMediaContainer) return;
|
||||||
|
|
||||||
|
// Очищаем контейнер
|
||||||
|
mainMediaContainer.innerHTML = '';
|
||||||
|
|
||||||
|
if (type === 'image') {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('data-lightbox', 'project-gallery');
|
||||||
|
link.setAttribute('data-title', caption);
|
||||||
|
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = url;
|
||||||
|
img.alt = caption;
|
||||||
|
img.className = 'main-media-img';
|
||||||
|
|
||||||
|
link.appendChild(img);
|
||||||
|
mainMediaContainer.appendChild(link);
|
||||||
|
} else if (type === 'video') {
|
||||||
|
const video = document.createElement('video');
|
||||||
|
video.controls = true;
|
||||||
|
video.className = 'main-media-video';
|
||||||
|
if (poster) {
|
||||||
|
video.poster = poster;
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = document.createElement('source');
|
||||||
|
source.src = url;
|
||||||
|
source.type = 'video/mp4';
|
||||||
|
|
||||||
|
video.appendChild(source);
|
||||||
|
video.appendChild(document.createTextNode('Ваш браузер не поддерживает видео.'));
|
||||||
|
|
||||||
|
mainMediaContainer.appendChild(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем активный thumbnail
|
||||||
|
document.querySelectorAll('.thumbnail-item').forEach(item => item.classList.remove('active'));
|
||||||
|
event.target.closest('.thumbnail-item').classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация Swiper для карусели
|
||||||
|
const swiper = new Swiper('.similarSwiper', {
|
||||||
|
effect: 'coverflow',
|
||||||
|
grabCursor: true,
|
||||||
|
centeredSlides: true,
|
||||||
|
slidesPerView: 'auto',
|
||||||
|
spaceBetween: 40,
|
||||||
|
loop: true,
|
||||||
|
coverflowEffect: {
|
||||||
|
rotate: 15,
|
||||||
|
stretch: 0,
|
||||||
|
depth: 200,
|
||||||
|
modifier: 1.5,
|
||||||
|
slideShadows: true,
|
||||||
|
},
|
||||||
|
navigation: {
|
||||||
|
nextEl: '.similar-next',
|
||||||
|
prevEl: '.similar-prev',
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
el: '.similar-pagination',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
breakpoints: {
|
||||||
|
768: {
|
||||||
|
slidesPerView: 2,
|
||||||
|
spaceBetween: 20,
|
||||||
|
},
|
||||||
|
1024: {
|
||||||
|
slidesPerView: 3,
|
||||||
|
spaceBetween: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
589
smartsoltech/static/assets/css/compact-gallery.css
Normal file
589
smartsoltech/static/assets/css/compact-gallery.css
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
/* Современная медиа-галерея */
|
||||||
|
.modern-media-gallery {
|
||||||
|
background: white;
|
||||||
|
border-radius: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Основное медиа */
|
||||||
|
.main-media-container {
|
||||||
|
position: relative;
|
||||||
|
aspect-ratio: 16/10;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-item {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-item.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-img,
|
||||||
|
.main-media-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-embed {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay с информацией */
|
||||||
|
.media-overlay {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
|
||||||
|
padding: 2rem;
|
||||||
|
color: white;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-item:hover .media-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-meta {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-action-btn {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-action-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Навигационные кнопки */
|
||||||
|
.media-nav-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #4f46e5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-media-wrapper:hover .media-nav-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev-btn {
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-nav-btn:hover {
|
||||||
|
background: white;
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Миниатюры */
|
||||||
|
.thumbnails-container {
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: #fafbfc;
|
||||||
|
border-top: 1px solid #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnails-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Для Firefox */
|
||||||
|
.thumbnails-wrapper {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #cbd5e0 transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Для Webkit браузеров */
|
||||||
|
.thumbnails-wrapper::-webkit-scrollbar {
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnails-wrapper::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnails-wrapper::-webkit-scrollbar-thumb {
|
||||||
|
background: #cbd5e0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item.active {
|
||||||
|
border-color: #4f46e5;
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail-placeholder,
|
||||||
|
.embed-thumbnail-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-type-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-type-badge.video {
|
||||||
|
background: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-type-badge.embed {
|
||||||
|
background: #06b6d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item:hover .thumbnail-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-number {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Индикатор прогресса */
|
||||||
|
.gallery-progress {
|
||||||
|
height: 4px;
|
||||||
|
background: #f1f5f9;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
|
transition: width 0.4s ease;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Placeholder для проектов без изображений */
|
||||||
|
.project-placeholder-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #64748b;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-placeholder-image i {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-placeholder-image p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.main-media-container {
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-overlay {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnails-container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
width: 70px;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modern-media-gallery {
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-overlay {
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-nav-btn {
|
||||||
|
opacity: 1;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev-btn {
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnails-container {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
width: 60px;
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-action-btn {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML-контент в описании проекта */
|
||||||
|
.description-text h1,
|
||||||
|
.description-text h2,
|
||||||
|
.description-text h3,
|
||||||
|
.description-text h4,
|
||||||
|
.description-text h5,
|
||||||
|
.description-text h6 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text h1 { font-size: 2rem; }
|
||||||
|
.description-text h2 { font-size: 1.75rem; }
|
||||||
|
.description-text h3 { font-size: 1.5rem; }
|
||||||
|
.description-text h4 { font-size: 1.25rem; }
|
||||||
|
.description-text h5 { font-size: 1.1rem; }
|
||||||
|
.description-text h6 { font-size: 1rem; }
|
||||||
|
|
||||||
|
.description-text p {
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text ul,
|
||||||
|
.description-text ol {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text blockquote {
|
||||||
|
border-left: 4px solid #4f46e5;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
margin: 2rem 0;
|
||||||
|
font-style: italic;
|
||||||
|
background: #f8fafc;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text code {
|
||||||
|
background: #f1f5f9;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Monaco', 'Menlo', monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #e53e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text pre {
|
||||||
|
background: #1a202c;
|
||||||
|
color: #fff;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text pre code {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text a {
|
||||||
|
color: #4f46e5;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text a:hover {
|
||||||
|
border-bottom-color: #4f46e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text strong,
|
||||||
|
.description-text b {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text em,
|
||||||
|
.description-text i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text hr {
|
||||||
|
border: none;
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
|
margin: 3rem 0;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 2rem 0;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text th,
|
||||||
|
.description-text td {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text th {
|
||||||
|
background: #f8fafc;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML-контент в технологиях */
|
||||||
|
.technologies-html-content {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content code {
|
||||||
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: 'Monaco', 'Menlo', monospace;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.2);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content code:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content p code {
|
||||||
|
margin: 0.2rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content h1,
|
||||||
|
.technologies-html-content h2,
|
||||||
|
.technologies-html-content h3,
|
||||||
|
.technologies-html-content h4,
|
||||||
|
.technologies-html-content h5,
|
||||||
|
.technologies-html-content h6 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content ul,
|
||||||
|
.technologies-html-content ol {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content li {
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content strong,
|
||||||
|
.technologies-html-content b {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a202c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content em,
|
||||||
|
.technologies-html-content i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content a {
|
||||||
|
color: #4f46e5;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologies-html-content a:hover {
|
||||||
|
border-bottom-color: #4f46e5;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
227
smartsoltech/static/assets/js/project-detail.js
Normal file
227
smartsoltech/static/assets/js/project-detail.js
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
// Modern Project Detail Page Enhancements
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
// Animate counter numbers
|
||||||
|
function animateCounters() {
|
||||||
|
const counters = document.querySelectorAll('.stat-number[data-target]');
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const counter = entry.target;
|
||||||
|
const target = parseInt(counter.dataset.target);
|
||||||
|
const duration = 2000; // 2 seconds
|
||||||
|
const step = target / (duration / 16); // 60fps
|
||||||
|
let current = 0;
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
current += step;
|
||||||
|
counter.textContent = Math.floor(current);
|
||||||
|
|
||||||
|
if (current >= target) {
|
||||||
|
counter.textContent = target;
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
}, 16);
|
||||||
|
|
||||||
|
observer.unobserve(counter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
threshold: 0.5
|
||||||
|
});
|
||||||
|
|
||||||
|
counters.forEach(counter => observer.observe(counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll-triggered animations
|
||||||
|
function initScrollAnimations() {
|
||||||
|
const animatedElements = document.querySelectorAll('.content-section, .tech-item, .info-item');
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach((entry, index) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setTimeout(() => {
|
||||||
|
entry.target.style.opacity = '1';
|
||||||
|
entry.target.style.transform = 'translateY(0)';
|
||||||
|
}, index * 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px 0px -50px 0px'
|
||||||
|
});
|
||||||
|
|
||||||
|
animatedElements.forEach(el => {
|
||||||
|
el.style.opacity = '0';
|
||||||
|
el.style.transform = 'translateY(30px)';
|
||||||
|
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share functionality
|
||||||
|
function initShareButton() {
|
||||||
|
const shareBtn = document.querySelector('.share-btn');
|
||||||
|
if (shareBtn) {
|
||||||
|
shareBtn.addEventListener('click', async function() {
|
||||||
|
const projectTitle = document.querySelector('.hero-title').textContent;
|
||||||
|
const url = window.location.href;
|
||||||
|
|
||||||
|
if (navigator.share) {
|
||||||
|
try {
|
||||||
|
await navigator.share({
|
||||||
|
title: projectTitle,
|
||||||
|
text: `Посмотрите на этот проект: ${projectTitle}`,
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Sharing failed:', err);
|
||||||
|
fallbackShare(url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fallbackShare(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallbackShare(url) {
|
||||||
|
// Copy to clipboard as fallback
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(url).then(() => {
|
||||||
|
showToast('Ссылка скопирована в буфер обмена!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showToast(message) {
|
||||||
|
// Create toast notification
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = 'toast-notification';
|
||||||
|
toast.textContent = message;
|
||||||
|
toast.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: linear-gradient(135deg, #48bb78, #38a169);
|
||||||
|
color: white;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||||
|
z-index: 10000;
|
||||||
|
transform: translateX(400px);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.style.transform = 'translateX(0)';
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.style.transform = 'translateX(400px)';
|
||||||
|
setTimeout(() => document.body.removeChild(toast), 300);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tech item hover effects
|
||||||
|
function initTechInteractions() {
|
||||||
|
const techItems = document.querySelectorAll('.tech-item');
|
||||||
|
|
||||||
|
techItems.forEach(item => {
|
||||||
|
item.addEventListener('mouseenter', function() {
|
||||||
|
this.style.transform = 'translateY(-8px) scale(1.02)';
|
||||||
|
});
|
||||||
|
|
||||||
|
item.addEventListener('mouseleave', function() {
|
||||||
|
this.style.transform = 'translateY(-5px) scale(1)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parallax effect for hero background
|
||||||
|
function initParallaxEffect() {
|
||||||
|
const heroBackground = document.querySelector('.hero-pattern');
|
||||||
|
if (!heroBackground) return;
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
function updateParallax() {
|
||||||
|
const scrolled = window.pageYOffset;
|
||||||
|
const rate = scrolled * -0.3;
|
||||||
|
|
||||||
|
heroBackground.style.transform = `translateY(${rate}px)`;
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestTick() {
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(updateParallax);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', requestTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth scroll for internal links
|
||||||
|
function initSmoothScroll() {
|
||||||
|
const links = document.querySelectorAll('a[href^="#"]');
|
||||||
|
|
||||||
|
links.forEach(link => {
|
||||||
|
link.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const targetId = this.getAttribute('href');
|
||||||
|
const targetSection = document.querySelector(targetId);
|
||||||
|
|
||||||
|
if (targetSection) {
|
||||||
|
targetSection.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize all enhancements
|
||||||
|
animateCounters();
|
||||||
|
initScrollAnimations();
|
||||||
|
initShareButton();
|
||||||
|
initTechInteractions();
|
||||||
|
initParallaxEffect();
|
||||||
|
initSmoothScroll();
|
||||||
|
|
||||||
|
// Add loading class removal after page load
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
document.body.classList.add('page-loaded');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// CSS for page loading animation
|
||||||
|
const loadingStyles = `
|
||||||
|
body:not(.page-loaded) .project-hero {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(50px);
|
||||||
|
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.page-loaded .project-hero {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-notification {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Inject loading styles
|
||||||
|
const styleSheet = document.createElement('style');
|
||||||
|
styleSheet.textContent = loadingStyles;
|
||||||
|
document.head.appendChild(styleSheet);
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 25 KiB |
@@ -179,7 +179,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
name='categories',
|
name='categories',
|
||||||
field=models.ManyToManyField(blank=True, related_name='projects', to='web.projectcategory', verbose_name='Категории'),
|
field=models.ManyToManyField(blank=True, related_name='projects', to='web.category', verbose_name='Категории'),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ProjectMedia',
|
name='ProjectMedia',
|
||||||
|
|||||||
@@ -38,11 +38,8 @@ class Migration(migrations.Migration):
|
|||||||
name='slug',
|
name='slug',
|
||||||
field=models.SlugField(blank=True, max_length=100, null=True, unique=True, verbose_name='URL'),
|
field=models.SlugField(blank=True, max_length=100, null=True, unique=True, verbose_name='URL'),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
# Удаляем проблемную операцию изменения ManyToManyField
|
||||||
model_name='project',
|
# Поле уже существует с нужными параметрами
|
||||||
name='categories',
|
|
||||||
field=models.ManyToManyField(blank=True, related_name='projects', to='web.category', verbose_name='Категории'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='projectmedia',
|
model_name='projectmedia',
|
||||||
name='project',
|
name='project',
|
||||||
|
|||||||
13
smartsoltech/web/migrations/0017_auto_20251126_0146.py
Normal file
13
smartsoltech/web/migrations/0017_auto_20251126_0146.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Generated by Django 5.1.1 on 2025-11-26 01:46
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web', '0016_delete_projectcategory_alter_category_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
]
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Fix for column name in project categories table
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web', '0017_auto_20251126_0146'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunSQL(
|
||||||
|
# Forward SQL - rename column and fix constraints
|
||||||
|
"""
|
||||||
|
-- Rename the column if it still exists as projectcategory_id
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT column_name
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'web_project_categories'
|
||||||
|
AND column_name = 'projectcategory_id'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE web_project_categories RENAME COLUMN projectcategory_id TO category_id;
|
||||||
|
|
||||||
|
-- Add foreign key constraint if it doesn't exist
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT constraint_name
|
||||||
|
FROM information_schema.table_constraints
|
||||||
|
WHERE table_name = 'web_project_categories'
|
||||||
|
AND constraint_name = 'web_project_categories_category_id_fk'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE web_project_categories
|
||||||
|
ADD CONSTRAINT web_project_categories_category_id_fk
|
||||||
|
FOREIGN KEY (category_id) REFERENCES web_category(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
""",
|
||||||
|
# Reverse SQL
|
||||||
|
"""
|
||||||
|
ALTER TABLE web_project_categories RENAME COLUMN category_id TO projectcategory_id;
|
||||||
|
ALTER TABLE web_project_categories DROP CONSTRAINT IF EXISTS web_project_categories_category_id_fk;
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
]
|
||||||
322
smartsoltech/web/templates/web/blog.html
Normal file
322
smartsoltech/web/templates/web/blog.html
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
{% extends 'web/base_modern.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}Блог - SmartSolTech{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="hero-modern">
|
||||||
|
<div class="container-modern">
|
||||||
|
<div class="row justify-content-center text-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1 class="display-4 fw-bold mb-4">
|
||||||
|
<span class="text-gradient">Блог</span> SmartSolTech
|
||||||
|
</h1>
|
||||||
|
<p class="lead text-muted mb-5">
|
||||||
|
Новости, статьи и инсайты из мира IT и технологий
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Blog Posts Section -->
|
||||||
|
<section class="section-padding">
|
||||||
|
<div class="container-modern">
|
||||||
|
{% if blog_posts %}
|
||||||
|
<div class="row g-4">
|
||||||
|
{% for post in blog_posts %}
|
||||||
|
<div class="col-lg-4 col-md-6">
|
||||||
|
<article class="card-modern h-100 hover-lift">
|
||||||
|
<!-- Post Image/Video -->
|
||||||
|
{% if post.video %}
|
||||||
|
<div style="height: 250px; overflow: hidden; border-radius: 15px 15px 0 0; position: relative;">
|
||||||
|
<video class="w-100 h-100"
|
||||||
|
style="object-fit: cover;"
|
||||||
|
muted
|
||||||
|
{% if post.video_poster %}poster="{{ post.video_poster.url }}"{% endif %}>
|
||||||
|
<source src="{{ post.video.url }}" type="video/mp4">
|
||||||
|
{% if post.image %}
|
||||||
|
<img src="{{ post.image.url }}" class="w-100 h-100" style="object-fit: cover;" alt="{{ post.title }}">
|
||||||
|
{% endif %}
|
||||||
|
</video>
|
||||||
|
<div class="position-absolute top-0 end-0 p-3">
|
||||||
|
<span class="badge bg-primary">
|
||||||
|
<i class="fas fa-play"></i> Видео
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="position-absolute bottom-0 start-0 end-0 p-3" style="background: linear-gradient(transparent, rgba(0,0,0,0.7));">
|
||||||
|
<button class="btn btn-light btn-sm" onclick="this.closest('div').previousElementSibling.play()">
|
||||||
|
<i class="fas fa-play me-1"></i> Воспроизвести
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif post.image %}
|
||||||
|
<div style="height: 250px; overflow: hidden; border-radius: 15px 15px 0 0;">
|
||||||
|
<img src="{{ post.image.url }}" alt="{{ post.title }}"
|
||||||
|
class="w-100 h-100"
|
||||||
|
style="object-fit: cover;"
|
||||||
|
loading="lazy">
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="w-100 bg-gradient d-flex align-items-center justify-content-center"
|
||||||
|
style="height: 250px; border-radius: 15px 15px 0 0;">
|
||||||
|
<i class="fas fa-newspaper text-white" style="font-size: 3rem; opacity: 0.7;"></i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Post Content -->
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<div class="mb-3">
|
||||||
|
<span class="badge bg-primary-modern">
|
||||||
|
<i class="fas fa-newspaper me-1"></i>
|
||||||
|
Блог
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="h5 mb-3 text-dark">
|
||||||
|
<a href="{% url 'blog_post_detail' post.pk %}"
|
||||||
|
class="text-decoration-none text-dark hover-primary">
|
||||||
|
{{ post.title }}
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="text-muted mb-3 flex-grow-1">
|
||||||
|
{% if post.content %}
|
||||||
|
<p>{{ post.content|striptags|truncatewords:20 }}</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Нет описания...</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Post Meta -->
|
||||||
|
<div class="d-flex justify-content-between align-items-center mt-auto pt-3 border-top">
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="fas fa-calendar-alt me-1"></i>
|
||||||
|
{{ post.published_date|date:"d.m.Y" }}
|
||||||
|
</small>
|
||||||
|
<a href="{% url 'blog_post_detail' post.pk %}"
|
||||||
|
class="btn btn-sm btn-outline-primary">
|
||||||
|
Читать далее
|
||||||
|
<i class="fas fa-arrow-right ms-1"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination (если планируется) -->
|
||||||
|
<div class="text-center mt-5">
|
||||||
|
<div class="d-inline-flex align-items-center gap-3">
|
||||||
|
<span class="text-muted">Показано {{ blog_posts.count }} из {{ blog_posts.count }} постов</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="mb-4">
|
||||||
|
<i class="fas fa-newspaper text-muted" style="font-size: 4rem;"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="h4 mb-3">Пока нет постов в блоге</h3>
|
||||||
|
<p class="text-muted mb-4">
|
||||||
|
Мы работаем над созданием интересного контента для вас
|
||||||
|
</p>
|
||||||
|
<a href="{% url 'home' %}" class="btn btn-primary-modern">
|
||||||
|
<i class="fas fa-home me-2"></i>
|
||||||
|
На главную
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Newsletter Section -->
|
||||||
|
<section class="section-padding bg-light">
|
||||||
|
<div class="container-modern">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6 text-center">
|
||||||
|
<div class="newsletter-cta">
|
||||||
|
<div class="mb-4">
|
||||||
|
<i class="fas fa-envelope text-primary" style="font-size: 3rem;"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="h3 mb-3">Следите за новостями</h2>
|
||||||
|
<p class="text-muted mb-4">
|
||||||
|
Подпишитесь на наши обновления, чтобы получать последние новости и статьи
|
||||||
|
</p>
|
||||||
|
<div class="d-flex justify-content-center gap-3 flex-wrap">
|
||||||
|
<a href="mailto:info@smartsoltech.kr" class="btn btn-primary-modern">
|
||||||
|
<i class="fas fa-envelope me-2"></i>
|
||||||
|
Связаться с нами
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-primary">
|
||||||
|
<i class="fas fa-cogs me-2"></i>
|
||||||
|
Наши услуги
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_scripts %}
|
||||||
|
<script>
|
||||||
|
// Smooth scrolling for blog navigation
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Video play on hover
|
||||||
|
const videoCards = document.querySelectorAll('video');
|
||||||
|
videoCards.forEach(video => {
|
||||||
|
video.addEventListener('mouseenter', function() {
|
||||||
|
this.currentTime = 0;
|
||||||
|
this.play().catch(e => console.log('Video autoplay prevented'));
|
||||||
|
});
|
||||||
|
|
||||||
|
video.addEventListener('mouseleave', function() {
|
||||||
|
this.pause();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enhanced card hover effects
|
||||||
|
const cards = document.querySelectorAll('.hover-lift');
|
||||||
|
cards.forEach(card => {
|
||||||
|
card.addEventListener('mouseenter', function() {
|
||||||
|
this.style.transform = 'translateY(-10px)';
|
||||||
|
});
|
||||||
|
|
||||||
|
card.addEventListener('mouseleave', function() {
|
||||||
|
this.style.transform = 'translateY(0)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search functionality (if needed later)
|
||||||
|
function searchBlogPosts(query) {
|
||||||
|
// Future implementation for blog search
|
||||||
|
console.log('Searching for:', query);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.hover-lift {
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-lift:hover {
|
||||||
|
box-shadow: 0 15px 40px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-primary:hover {
|
||||||
|
color: var(--primary-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-modern article {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newsletter-cta {
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blog post content styling */
|
||||||
|
.text-muted p {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Video overlay improvements */
|
||||||
|
video {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
video:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsiveness */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-modern .display-4 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newsletter-cta {
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-flex.justify-content-center.gap-3 {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-flex.justify-content-center.gap-3 .btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print styles */
|
||||||
|
@media print {
|
||||||
|
.newsletter-cta,
|
||||||
|
.btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced accessibility */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.hover-lift,
|
||||||
|
video {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
video:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-lift:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus styles for better accessibility */
|
||||||
|
.hover-primary:focus {
|
||||||
|
outline: 2px solid var(--primary-color);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading states */
|
||||||
|
.card-modern img {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-modern img:not([src]) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced video controls */
|
||||||
|
.position-absolute .btn:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar for mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.container-modern {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
399
smartsoltech/web/templates/web/blog_post_detail.html
Normal file
399
smartsoltech/web/templates/web/blog_post_detail.html
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
{% extends 'web/base_modern.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}{{ blog_post.title }} - SmartSolTech{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="hero-modern">
|
||||||
|
<div class="container-modern">
|
||||||
|
<div class="row gy-4 align-items-center">
|
||||||
|
<div class="col-lg-8 mx-auto text-center">
|
||||||
|
<div class="blog-header">
|
||||||
|
<div class="mb-3">
|
||||||
|
<span class="badge bg-primary-modern">
|
||||||
|
<i class="fas fa-newspaper me-1"></i>
|
||||||
|
Блог
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="display-5 fw-bold mb-4">{{ blog_post.title }}</h1>
|
||||||
|
<div class="blog-meta d-flex justify-content-center align-items-center flex-wrap gap-3 text-muted">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fas fa-calendar-alt me-2"></i>
|
||||||
|
<span>{{ blog_post.published_date|date:"d.m.Y" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fas fa-clock me-2"></i>
|
||||||
|
<span>{{ blog_post.published_date|date:"H:i" }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Blog Content -->
|
||||||
|
<section class="section-padding">
|
||||||
|
<div class="container-modern">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<!-- Featured Image/Video -->
|
||||||
|
{% if blog_post.video %}
|
||||||
|
<div class="mb-5">
|
||||||
|
<video class="w-100 rounded-4 shadow-lg"
|
||||||
|
style="max-height: 500px; object-fit: cover;"
|
||||||
|
controls
|
||||||
|
{% if blog_post.video_poster %}poster="{{ blog_post.video_poster.url }}"{% endif %}>
|
||||||
|
<source src="{{ blog_post.video.url }}" type="video/mp4">
|
||||||
|
{% if blog_post.image %}
|
||||||
|
<!-- Fallback image if video not supported -->
|
||||||
|
<img src="{{ blog_post.image.url }}" class="w-100 rounded-4 shadow-lg" style="max-height: 500px; object-fit: cover;" alt="{{ blog_post.title }}">
|
||||||
|
{% endif %}
|
||||||
|
Ваш браузер не поддерживает видео.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
{% elif blog_post.image %}
|
||||||
|
<div class="mb-5">
|
||||||
|
<img class="w-100 rounded-4 shadow-lg"
|
||||||
|
style="max-height: 500px; object-fit: cover;"
|
||||||
|
src="{{ blog_post.image.url }}"
|
||||||
|
alt="{{ blog_post.title }}" />
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Blog Content -->
|
||||||
|
<div class="blog-content">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
{{ blog_post.content|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Blog Navigation -->
|
||||||
|
<div class="blog-navigation mt-5 pt-4 border-top">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<a href="{% url 'home' %}#blog" class="btn btn-outline-primary">
|
||||||
|
<i class="fas fa-arrow-left me-2"></i>
|
||||||
|
Вернуться к блогу
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col text-end">
|
||||||
|
<!-- Social Share Buttons -->
|
||||||
|
<div class="share-buttons">
|
||||||
|
<span class="text-muted me-3">Поделиться:</span>
|
||||||
|
<a href="javascript:void(0)"
|
||||||
|
onclick="shareToSocial('telegram')"
|
||||||
|
class="btn btn-sm btn-outline-primary me-2"
|
||||||
|
title="Поделиться в Telegram">
|
||||||
|
<i class="fab fa-telegram"></i>
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)"
|
||||||
|
onclick="shareToSocial('whatsapp')"
|
||||||
|
class="btn btn-sm btn-outline-success me-2"
|
||||||
|
title="Поделиться в WhatsApp">
|
||||||
|
<i class="fab fa-whatsapp"></i>
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)"
|
||||||
|
onclick="shareToSocial('copy')"
|
||||||
|
class="btn btn-sm btn-outline-secondary"
|
||||||
|
title="Скопировать ссылку">
|
||||||
|
<i class="fas fa-copy"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="blog-sidebar">
|
||||||
|
<!-- Contact CTA -->
|
||||||
|
<div class="card-modern mb-4">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div class="mb-3">
|
||||||
|
<i class="fas fa-comments text-primary" style="font-size: 2rem;"></i>
|
||||||
|
</div>
|
||||||
|
<h5 class="mb-3">Есть вопросы?</h5>
|
||||||
|
<p class="text-muted mb-4">
|
||||||
|
Свяжитесь с нами для бесплатной консультации
|
||||||
|
</p>
|
||||||
|
<a href="mailto:info@smartsoltech.kr" class="btn btn-primary-modern">
|
||||||
|
<i class="fas fa-envelope me-2"></i>
|
||||||
|
Написать нам
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Services CTA -->
|
||||||
|
<div class="card-modern">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="mb-3">
|
||||||
|
<i class="fas fa-cogs text-primary me-2"></i>
|
||||||
|
Наши услуги
|
||||||
|
</h6>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-primary btn-sm">
|
||||||
|
<i class="fas fa-laptop-code me-2"></i>
|
||||||
|
Веб-разработка
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-primary btn-sm">
|
||||||
|
<i class="fas fa-mobile-alt me-2"></i>
|
||||||
|
Мобильные приложения
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-primary btn-sm">
|
||||||
|
<i class="fas fa-chart-line me-2"></i>
|
||||||
|
IT консалтинг
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-primary btn-sm">
|
||||||
|
<i class="fas fa-cloud me-2"></i>
|
||||||
|
Облачные решения
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- CTA Section -->
|
||||||
|
<section class="section-padding bg-gradient text-white">
|
||||||
|
<div class="container-modern text-center">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<h2 class="display-6 fw-bold mb-4">
|
||||||
|
Готовы обсудить ваш проект?
|
||||||
|
</h2>
|
||||||
|
<p class="lead mb-5 opacity-90">
|
||||||
|
Получите бесплатную консультацию от наших экспертов
|
||||||
|
</p>
|
||||||
|
<div class="d-flex flex-wrap gap-3 justify-content-center">
|
||||||
|
<a href="mailto:info@smartsoltech.kr" class="btn btn-light btn-lg text-primary">
|
||||||
|
<i class="fas fa-envelope me-2"></i>
|
||||||
|
Связаться с нами
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'services' %}" class="btn btn-outline-light btn-lg">
|
||||||
|
<i class="fas fa-th-large me-2"></i>
|
||||||
|
Наши услуги
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_scripts %}
|
||||||
|
<script>
|
||||||
|
// Social sharing functionality
|
||||||
|
function shareToSocial(platform) {
|
||||||
|
const url = encodeURIComponent(window.location.href);
|
||||||
|
const title = encodeURIComponent('{{ blog_post.title|addslashes }}');
|
||||||
|
const text = encodeURIComponent('{{ blog_post.title|addslashes }} - SmartSolTech');
|
||||||
|
|
||||||
|
let shareUrl = '';
|
||||||
|
|
||||||
|
switch(platform) {
|
||||||
|
case 'telegram':
|
||||||
|
shareUrl = `https://t.me/share/url?url=${url}&text=${text}`;
|
||||||
|
break;
|
||||||
|
case 'whatsapp':
|
||||||
|
shareUrl = `https://wa.me/?text=${text}%20${url}`;
|
||||||
|
break;
|
||||||
|
case 'copy':
|
||||||
|
navigator.clipboard.writeText(window.location.href).then(() => {
|
||||||
|
// Show success message
|
||||||
|
const btn = event.target.closest('a');
|
||||||
|
const originalIcon = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<i class="fas fa-check"></i>';
|
||||||
|
btn.classList.add('btn-success');
|
||||||
|
btn.classList.remove('btn-outline-secondary');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.innerHTML = originalIcon;
|
||||||
|
btn.classList.remove('btn-success');
|
||||||
|
btn.classList.add('btn-outline-secondary');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to copy: ', err);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shareUrl) {
|
||||||
|
window.open(shareUrl, '_blank', 'width=550,height=420');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth scrolling for anchor links
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = document.querySelector(this.getAttribute('href'));
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.blog-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-meta {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content {
|
||||||
|
line-height: 1.8;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content h1,
|
||||||
|
.blog-content h2,
|
||||||
|
.blog-content h3,
|
||||||
|
.blog-content h4,
|
||||||
|
.blog-content h5,
|
||||||
|
.blog-content h6 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content p {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content blockquote {
|
||||||
|
border-left: 4px solid var(--primary-color);
|
||||||
|
background: var(--bg-light);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content ul,
|
||||||
|
.blog-content ol {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content pre {
|
||||||
|
background: var(--bg-dark);
|
||||||
|
color: var(--text-light);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content code {
|
||||||
|
background: var(--bg-light);
|
||||||
|
color: var(--primary-color);
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content pre code {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-sidebar {
|
||||||
|
position: sticky;
|
||||||
|
top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-buttons a {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-buttons a:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-navigation {
|
||||||
|
background: var(--bg-light);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content typography improvements */
|
||||||
|
.content-wrapper {
|
||||||
|
font-family: 'Georgia', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper h1 { font-size: 2.5rem; }
|
||||||
|
.content-wrapper h2 { font-size: 2rem; }
|
||||||
|
.content-wrapper h3 { font-size: 1.5rem; }
|
||||||
|
.content-wrapper h4 { font-size: 1.25rem; }
|
||||||
|
.content-wrapper h5 { font-size: 1.1rem; }
|
||||||
|
.content-wrapper h6 { font-size: 1rem; }
|
||||||
|
|
||||||
|
/* Responsive improvements */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.blog-header h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-meta {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-buttons {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-buttons span {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-sidebar {
|
||||||
|
position: relative;
|
||||||
|
top: auto;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper h1 { font-size: 1.8rem; }
|
||||||
|
.content-wrapper h2 { font-size: 1.5rem; }
|
||||||
|
.content-wrapper h3 { font-size: 1.3rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print styles */
|
||||||
|
@media print {
|
||||||
|
.blog-navigation,
|
||||||
|
.blog-sidebar,
|
||||||
|
.share-buttons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content {
|
||||||
|
font-size: 12pt;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
371
smartsoltech/web/templates/web/gallery_preview.html
Normal file
371
smartsoltech/web/templates/web/gallery_preview.html
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Предпросмотр современной галереи</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- FontAwesome -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
|
||||||
|
<!-- Lightbox2 CSS -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/css/lightbox.min.css">
|
||||||
|
|
||||||
|
<!-- Наши стили -->
|
||||||
|
<link href="../../static/assets/css/compact-gallery.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #f8fafc;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e293b;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="preview-header">
|
||||||
|
<h1>Современная медиа-галерея</h1>
|
||||||
|
<p>Интерактивная галерея с навигацией, миниатюрами и полноэкранным просмотром</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Современная медиа-галерея -->
|
||||||
|
<div class="modern-media-gallery">
|
||||||
|
<!-- Основное медиа -->
|
||||||
|
<div class="main-media-container">
|
||||||
|
<div class="main-media-wrapper">
|
||||||
|
<!-- Медиа элементы -->
|
||||||
|
<div class="main-media-item active" data-index="0">
|
||||||
|
<img src="https://via.placeholder.com/800x500/4f46e5/ffffff?text=Главное+изображение+1"
|
||||||
|
alt="Изображение 1" class="main-media-img">
|
||||||
|
<div class="media-overlay">
|
||||||
|
<div class="media-info">
|
||||||
|
<div class="media-title">Название изображения</div>
|
||||||
|
<div class="media-meta">Фото • 1920x1080 • 2.3 MB</div>
|
||||||
|
</div>
|
||||||
|
<button class="media-action-btn" onclick="openLightbox(0)">
|
||||||
|
<i class="fas fa-expand"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-media-item" data-index="1">
|
||||||
|
<img src="https://via.placeholder.com/800x500/7c3aed/ffffff?text=Главное+изображение+2"
|
||||||
|
alt="Изображение 2" class="main-media-img">
|
||||||
|
<div class="media-overlay">
|
||||||
|
<div class="media-info">
|
||||||
|
<div class="media-title">Второе изображение</div>
|
||||||
|
<div class="media-meta">Фото • 1920x1080 • 1.8 MB</div>
|
||||||
|
</div>
|
||||||
|
<button class="media-action-btn" onclick="openLightbox(1)">
|
||||||
|
<i class="fas fa-expand"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-media-item" data-index="2">
|
||||||
|
<video class="main-media-video" controls>
|
||||||
|
<source src="https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
<div class="media-overlay">
|
||||||
|
<div class="media-info">
|
||||||
|
<div class="media-title">Демо видео</div>
|
||||||
|
<div class="media-meta">Видео • MP4 • 1:32</div>
|
||||||
|
</div>
|
||||||
|
<button class="media-action-btn" onclick="toggleVideo(2)">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-media-item" data-index="3">
|
||||||
|
<div class="embed-container">
|
||||||
|
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
||||||
|
class="main-media-embed"
|
||||||
|
allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="media-overlay">
|
||||||
|
<div class="media-info">
|
||||||
|
<div class="media-title">YouTube видео</div>
|
||||||
|
<div class="media-meta">Встраивание • YouTube</div>
|
||||||
|
</div>
|
||||||
|
<button class="media-action-btn" onclick="openFullscreen(3)">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-media-item" data-index="4">
|
||||||
|
<img src="https://via.placeholder.com/800x500/06b6d4/ffffff?text=Главное+изображение+3"
|
||||||
|
alt="Изображение 3" class="main-media-img">
|
||||||
|
<div class="media-overlay">
|
||||||
|
<div class="media-info">
|
||||||
|
<div class="media-title">Третье изображение</div>
|
||||||
|
<div class="media-meta">Фото • 1920x1080 • 3.1 MB</div>
|
||||||
|
</div>
|
||||||
|
<button class="media-action-btn" onclick="openLightbox(4)">
|
||||||
|
<i class="fas fa-expand"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Навигационные кнопки -->
|
||||||
|
<button class="media-nav-btn prev-btn" onclick="gallery.previousMedia()">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="media-nav-btn next-btn" onclick="gallery.nextMedia()">
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Индикатор прогресса -->
|
||||||
|
<div class="gallery-progress">
|
||||||
|
<div class="progress-bar" style="width: 20%"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Миниатюры -->
|
||||||
|
<div class="thumbnails-container">
|
||||||
|
<div class="thumbnails-wrapper">
|
||||||
|
<div class="thumbnail-item active" data-index="0" onclick="gallery.switchToMedia(0)">
|
||||||
|
<img src="https://via.placeholder.com/80x60/4f46e5/ffffff?text=1" alt="Thumb 1" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<span class="thumbnail-number">1</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="1" onclick="gallery.switchToMedia(1)">
|
||||||
|
<img src="https://via.placeholder.com/80x60/7c3aed/ffffff?text=2" alt="Thumb 2" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<span class="thumbnail-number">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="2" onclick="gallery.switchToMedia(2)">
|
||||||
|
<div class="video-thumbnail-placeholder">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</div>
|
||||||
|
<div class="media-type-badge video">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</div>
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<span class="thumbnail-number">3</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="3" onclick="gallery.switchToMedia(3)">
|
||||||
|
<div class="embed-thumbnail-placeholder">
|
||||||
|
<i class="fab fa-youtube"></i>
|
||||||
|
</div>
|
||||||
|
<div class="media-type-badge embed">
|
||||||
|
<i class="fas fa-link"></i>
|
||||||
|
</div>
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<span class="thumbnail-number">4</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail-item" data-index="4" onclick="gallery.switchToMedia(4)">
|
||||||
|
<img src="https://via.placeholder.com/80x60/06b6d4/ffffff?text=3" alt="Thumb 3" class="thumbnail-img">
|
||||||
|
<div class="thumbnail-overlay">
|
||||||
|
<span class="thumbnail-number">5</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- jQuery -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Lightbox2 JS -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/js/lightbox.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Класс для управления современной галереей
|
||||||
|
class ModernMediaGallery {
|
||||||
|
constructor(container) {
|
||||||
|
this.container = container;
|
||||||
|
this.mediaItems = container.querySelectorAll('.main-media-item');
|
||||||
|
this.thumbnails = container.querySelectorAll('.thumbnail-item');
|
||||||
|
this.progressBar = container.querySelector('.progress-bar');
|
||||||
|
this.currentIndex = 0;
|
||||||
|
|
||||||
|
this.initGallery();
|
||||||
|
this.bindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
initGallery() {
|
||||||
|
this.updateProgress();
|
||||||
|
this.preloadMedia();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
// Клавиатурные события
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'ArrowLeft') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.previousMedia();
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.nextMedia();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Touch события для свайпа
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
|
||||||
|
this.container.addEventListener('touchstart', (e) => {
|
||||||
|
startX = e.touches[0].clientX;
|
||||||
|
startY = e.touches[0].clientY;
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
this.container.addEventListener('touchend', (e) => {
|
||||||
|
const endX = e.changedTouches[0].clientX;
|
||||||
|
const endY = e.changedTouches[0].clientY;
|
||||||
|
|
||||||
|
const deltaX = endX - startX;
|
||||||
|
const deltaY = endY - startY;
|
||||||
|
|
||||||
|
// Проверяем, что это горизонтальный свайп
|
||||||
|
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
|
||||||
|
if (deltaX > 0) {
|
||||||
|
this.previousMedia();
|
||||||
|
} else {
|
||||||
|
this.nextMedia();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToMedia(index) {
|
||||||
|
if (index === this.currentIndex) return;
|
||||||
|
|
||||||
|
// Останавливаем текущее видео
|
||||||
|
this.stopCurrentVideo();
|
||||||
|
|
||||||
|
// Обновляем активные элементы
|
||||||
|
this.mediaItems[this.currentIndex].classList.remove('active');
|
||||||
|
this.thumbnails[this.currentIndex].classList.remove('active');
|
||||||
|
|
||||||
|
this.currentIndex = index;
|
||||||
|
|
||||||
|
this.mediaItems[this.currentIndex].classList.add('active');
|
||||||
|
this.thumbnails[this.currentIndex].classList.add('active');
|
||||||
|
|
||||||
|
this.updateProgress();
|
||||||
|
this.scrollToActiveThumbnail();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextMedia() {
|
||||||
|
const nextIndex = (this.currentIndex + 1) % this.mediaItems.length;
|
||||||
|
this.switchToMedia(nextIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousMedia() {
|
||||||
|
const prevIndex = (this.currentIndex - 1 + this.mediaItems.length) % this.mediaItems.length;
|
||||||
|
this.switchToMedia(prevIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress() {
|
||||||
|
const progress = ((this.currentIndex + 1) / this.mediaItems.length) * 100;
|
||||||
|
this.progressBar.style.width = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToActiveThumbnail() {
|
||||||
|
const activeThumbnail = this.thumbnails[this.currentIndex];
|
||||||
|
const container = activeThumbnail.parentElement;
|
||||||
|
|
||||||
|
const thumbnailOffsetLeft = activeThumbnail.offsetLeft;
|
||||||
|
const thumbnailWidth = activeThumbnail.offsetWidth;
|
||||||
|
const containerWidth = container.offsetWidth;
|
||||||
|
|
||||||
|
const scrollLeft = thumbnailOffsetLeft - (containerWidth / 2) + (thumbnailWidth / 2);
|
||||||
|
container.scrollTo({
|
||||||
|
left: scrollLeft,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCurrentVideo() {
|
||||||
|
const currentItem = this.mediaItems[this.currentIndex];
|
||||||
|
const video = currentItem.querySelector('video');
|
||||||
|
if (video) {
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preloadMedia() {
|
||||||
|
this.mediaItems.forEach((item, index) => {
|
||||||
|
if (index <= 2) { // Предзагружаем первые 3 элемента
|
||||||
|
const img = item.querySelector('img');
|
||||||
|
if (img && !img.complete) {
|
||||||
|
// Изображение уже загружается
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация галереи
|
||||||
|
let gallery;
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const galleryContainer = document.querySelector('.modern-media-gallery');
|
||||||
|
if (galleryContainer) {
|
||||||
|
gallery = new ModernMediaGallery(galleryContainer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Глобальные функции для интерактивности
|
||||||
|
function openLightbox(index) {
|
||||||
|
// Функция для открытия изображения в лайтбоксе
|
||||||
|
console.log('Открытие лайтбокса для изображения', index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleVideo(index) {
|
||||||
|
const video = gallery.mediaItems[index].querySelector('video');
|
||||||
|
if (video.paused) {
|
||||||
|
video.play();
|
||||||
|
} else {
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFullscreen(index) {
|
||||||
|
// Функция для открытия встраиваемого контента в полном экране
|
||||||
|
console.log('Открытие в полном экране', index);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -901,60 +901,64 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Показываем текст активного индикатора
|
// Показываем текст активного индикатора
|
||||||
const title = indicator.querySelector('.pill-indicator-title');
|
const title = indicator.querySelector('.pill-indicator-title');
|
||||||
if (title) {
|
if (title) {
|
||||||
indicator.style.color = '#333';
|
// Убираем inline стили, чтобы CSS правила работали корректно
|
||||||
title.style.opacity = '1';
|
indicator.style.color = '';
|
||||||
title.style.transform = 'scale(1)';
|
title.style.opacity = '';
|
||||||
|
title.style.transform = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
indicator.classList.remove('active');
|
indicator.classList.remove('active');
|
||||||
// Скрываем текст неактивных индикаторов
|
// Скрываем текст неактивных индикаторов и убираем inline стили
|
||||||
const title = indicator.querySelector('.pill-indicator-title');
|
const title = indicator.querySelector('.pill-indicator-title');
|
||||||
if (title) {
|
if (title) {
|
||||||
indicator.style.color = 'transparent';
|
indicator.style.color = '';
|
||||||
title.style.opacity = '0';
|
title.style.opacity = '';
|
||||||
title.style.transform = 'scale(0.8)';
|
title.style.transform = '';
|
||||||
|
// Убираем любые hover эффекты
|
||||||
|
indicator.style.transform = '';
|
||||||
|
indicator.style.background = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Обновляем ширину внешней пилюли
|
// Динамический расчет ширины внешнего контейнера
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (outerPill) {
|
if (outerPill && indicators.length > 0) {
|
||||||
const activeIndicator = indicators[index];
|
let totalWidth = 0;
|
||||||
|
|
||||||
if (activeIndicator) {
|
// Проходим по всем маркерам и суммируем их ширины
|
||||||
// Даем время для применения стилей активного элемента
|
indicators.forEach((indicator, i) => {
|
||||||
setTimeout(() => {
|
if (i === index && indicator.classList.contains('active')) {
|
||||||
// Вычисляем ширину активной пилюли на основе текста
|
// Активный маркер - измеряем его реальную ширину
|
||||||
const titleElement = activeIndicator.querySelector('.pill-indicator-title');
|
const rect = indicator.getBoundingClientRect();
|
||||||
let activePillWidth = 80; // минимальная ширина активного элемента
|
totalWidth += rect.width || 60; // fallback к минимальной ширине
|
||||||
|
} else {
|
||||||
if (titleElement && titleElement.textContent) {
|
// Неактивный маркер - фиксированная ширина 36px
|
||||||
// Формула: длина текста * 8px + padding (32px) + min-width
|
totalWidth += 36;
|
||||||
const textLength = titleElement.textContent.length;
|
|
||||||
activePillWidth = Math.max(textLength * 8 + 32, 80);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Количество неактивных маркеров
|
// Добавляем gap между маркерами (16px), кроме последнего
|
||||||
const inactiveCount = indicators.length - 1;
|
if (i < indicators.length - 1) {
|
||||||
|
totalWidth += 16;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Ширина неактивных элементов: 32px каждый + margin 4px между ними
|
// Добавляем padding контейнера: 10px слева + 10px справа
|
||||||
const inactiveWidth = inactiveCount * 32 + (inactiveCount > 0 ? inactiveCount * 4 : 0);
|
totalWidth += 20;
|
||||||
|
|
||||||
// Margin активного элемента: 16px (8px с каждой стороны)
|
|
||||||
const activeMargin = inactiveCount > 0 ? 16 : 0;
|
|
||||||
|
|
||||||
// Общая ширина: активный + неактивные + margin + padding контейнера
|
|
||||||
const totalWidth = activePillWidth + inactiveWidth + activeMargin + 32;
|
|
||||||
|
|
||||||
console.log('Active pill width:', activePillWidth, 'Inactive width:', inactiveWidth, 'Total:', totalWidth);
|
|
||||||
|
|
||||||
|
// Применяем новую ширину
|
||||||
outerPill.style.width = totalWidth + 'px';
|
outerPill.style.width = totalWidth + 'px';
|
||||||
outerPill.style.transition = 'all 0.4s cubic-bezier(0.23, 1, 0.32, 1)';
|
|
||||||
|
console.log('Pill state update:');
|
||||||
|
console.log('- Active index:', index);
|
||||||
|
console.log('- Total width:', totalWidth + 'px');
|
||||||
|
console.log('- Active element width:',
|
||||||
|
indicators[index] ? indicators[index].getBoundingClientRect().width + 'px' : 'N/A');
|
||||||
|
console.log('- Inactive elements count:', indicators.length - 1);
|
||||||
|
console.log('- Gaps total:', (indicators.length - 1) * 16 + 'px');
|
||||||
|
console.log('- Padding total: 20px');
|
||||||
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
currentActiveIndex = index;
|
currentActiveIndex = index;
|
||||||
}
|
}
|
||||||
@@ -981,19 +985,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (!this.classList.contains('active')) {
|
if (!this.classList.contains('active')) {
|
||||||
this.style.transform = 'scale(1.1)';
|
this.style.transform = 'scale(1.1)';
|
||||||
this.style.background = 'rgba(255, 255, 255, 0.6)';
|
this.style.background = 'rgba(255, 255, 255, 0.6)';
|
||||||
|
this.style.borderColor = 'rgba(255, 255, 255, 0.7)';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
indicator.addEventListener('mouseleave', function() {
|
indicator.addEventListener('mouseleave', function() {
|
||||||
if (!this.classList.contains('active')) {
|
if (!this.classList.contains('active')) {
|
||||||
this.style.transform = 'scale(1)';
|
// Возвращаем к исходному состоянию
|
||||||
this.style.background = 'rgba(255, 255, 255, 0.4)';
|
this.style.transform = '';
|
||||||
|
this.style.background = '';
|
||||||
|
this.style.borderColor = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bootstrap carousel события
|
// Bootstrap carousel события
|
||||||
if (carousel) {
|
if (carousel) {
|
||||||
|
// Обработка начала смены слайда
|
||||||
carousel.addEventListener('slide.bs.carousel', function(event) {
|
carousel.addEventListener('slide.bs.carousel', function(event) {
|
||||||
const nextIndex = event.to;
|
const nextIndex = event.to;
|
||||||
console.log('Carousel sliding to:', nextIndex);
|
console.log('Carousel sliding to:', nextIndex);
|
||||||
@@ -1007,27 +1015,76 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем состояние пилюли сразу при начале смены слайда
|
||||||
updatePillState(nextIndex);
|
updatePillState(nextIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Инициализируем первое состояние
|
// Дополнительная обработка завершения смены слайда для надежности
|
||||||
|
carousel.addEventListener('slid.bs.carousel', function(event) {
|
||||||
|
const currentIndex = event.to;
|
||||||
|
console.log('Carousel slide completed:', currentIndex);
|
||||||
|
|
||||||
|
// Дополнительное обновление состояния для гарантии корректного отображения
|
||||||
|
setTimeout(() => {
|
||||||
|
updatePillState(currentIndex);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Инициализируем первое состояние и рассчитываем начальную ширину
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('Initializing pill state...');
|
console.log('Initializing pill state...');
|
||||||
updatePillState(0);
|
updatePillState(0);
|
||||||
|
|
||||||
|
// Дополнительная инициализация ширины контейнера
|
||||||
|
if (outerPill && indicators.length > 0) {
|
||||||
|
let initialWidth = 20; // padding
|
||||||
|
|
||||||
|
// Первый элемент активный, остальные неактивные
|
||||||
|
indicators.forEach((indicator, i) => {
|
||||||
|
if (i === 0) {
|
||||||
|
// Даем время активному элементу развернуться
|
||||||
|
setTimeout(() => {
|
||||||
|
const rect = indicator.getBoundingClientRect();
|
||||||
|
let width = 20 + rect.width; // padding + активный элемент
|
||||||
|
|
||||||
|
// Добавляем неактивные элементы
|
||||||
|
if (indicators.length > 1) {
|
||||||
|
width += (indicators.length - 1) * 36; // неактивные элементы
|
||||||
|
width += (indicators.length - 1) * 16; // gaps между элементами
|
||||||
|
}
|
||||||
|
|
||||||
|
outerPill.style.width = width + 'px';
|
||||||
|
console.log('Initial container width:', width + 'px');
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отслеживаем изменения размеров активного элемента
|
// Отслеживаем изменения размеров активного элемента для пересчета ширины
|
||||||
if (window.ResizeObserver) {
|
if (window.ResizeObserver && outerPill) {
|
||||||
const resizeObserver = new ResizeObserver(entries => {
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
if (outerPill) {
|
// Пересчитываем ширину контейнера при изменении размеров
|
||||||
const activeIndicator = indicators[currentActiveIndex];
|
let totalWidth = 0;
|
||||||
if (activeIndicator && activeIndicator.classList.contains('active')) {
|
|
||||||
updatePillState(currentActiveIndex);
|
indicators.forEach((indicator, i) => {
|
||||||
|
if (indicator.classList.contains('active')) {
|
||||||
|
const rect = indicator.getBoundingClientRect();
|
||||||
|
totalWidth += rect.width;
|
||||||
|
} else {
|
||||||
|
totalWidth += 36;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i < indicators.length - 1) {
|
||||||
|
totalWidth += 16;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
totalWidth += 20; // padding
|
||||||
|
outerPill.style.width = totalWidth + 'px';
|
||||||
|
});
|
||||||
|
|
||||||
indicators.forEach(indicator => {
|
indicators.forEach(indicator => {
|
||||||
resizeObserver.observe(indicator);
|
resizeObserver.observe(indicator);
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -49,88 +49,139 @@
|
|||||||
|
|
||||||
.projects-grid {
|
.projects-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
|
max-width: 100%;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
/* Ограничиваем максимальный размер карточки */
|
||||||
.projects-grid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
|
||||||
.projects-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card {
|
.project-card {
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100%;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
box-shadow: 0 8px 25px rgba(0,0,0,0.08);
|
||||||
transition: box-shadow 0.3s ease !important;
|
transition: all 0.3s ease;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card:hover {
|
.project-card:hover {
|
||||||
box-shadow: 0 15px 40px rgba(0,0,0,0.2) !important;
|
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
|
||||||
transform: none !important;
|
transform: translateY(-5px);
|
||||||
|
border-color: #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-thumbnail {
|
/* Верхняя часть карточки - медиа контент */
|
||||||
|
.project-media {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 180px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, #667eea20 0%, #764ba220 100%);
|
background: linear-gradient(135deg, #667eea10 0%, #764ba210 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-thumbnail img {
|
.project-media img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
object-position: center;
|
||||||
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
will-change: transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card:hover .project-thumbnail img {
|
.project-card:hover .project-media img {
|
||||||
transform: scale(1.15) translateY(-10px) !important;
|
transform: scale(1.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-thumbnail::after {
|
.project-media video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Заглушка для проектов без изображения */
|
||||||
|
.project-media.no-image {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-media.no-image::before {
|
||||||
|
content: '\f1c2'; /* FontAwesome folder icon */
|
||||||
|
font-family: 'Font Awesome 5 Free';
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-media::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 50%;
|
height: 40%;
|
||||||
background: linear-gradient(to top, rgba(0,0,0,0.3), transparent);
|
background: linear-gradient(to top, rgba(0,0,0,0.4), transparent);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Медиа плеер для видео */
|
||||||
|
.media-player {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #667eea;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-btn:hover {
|
||||||
|
background: white;
|
||||||
|
transform: translate(-50%, -50%) scale(1.1);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
.project-badge {
|
.project-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 15px;
|
||||||
right: 10px;
|
right: 15px;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.35rem 0.75rem;
|
padding: 0.4rem 0.8rem;
|
||||||
border-radius: 15px;
|
border-radius: 20px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
z-index: 1;
|
z-index: 2;
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
box-shadow: 0 4px 15px rgba(0,0,0,0.25);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Основной контент карточки */
|
||||||
.project-content {
|
.project-content {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -141,66 +192,135 @@
|
|||||||
.project-title {
|
.project-title {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #2d3748;
|
color: #1a202c;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-height: 2.6rem; /* Фиксированная высота для выравнивания */
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-description {
|
.project-description {
|
||||||
color: #718096;
|
color: #64748b;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-size: 0.9rem;
|
font-size: 0.85rem;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-height: 2.5rem; /* Фиксированная высота для выравнивания */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Статистика проекта */
|
||||||
|
.project-stats-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item i {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-year {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Футер карточки - категории и дополнительная информация */
|
||||||
|
.project-footer {
|
||||||
|
padding: 0 1rem 1rem;
|
||||||
|
border-top: 1px solid #f1f5f9;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-categories {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.35rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
||||||
|
color: #667eea;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-tag i {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-meta {
|
.project-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 0.75rem;
|
|
||||||
border-top: 1px solid #e2e8f0;
|
|
||||||
margin-top: auto;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5rem;
|
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
color: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-stats {
|
.project-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.75rem;
|
gap: 1rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.8rem;
|
||||||
color: #718096;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-stats i {
|
.project-info span {
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-categories {
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
align-items: center;
|
||||||
gap: 0.35rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-tag {
|
.project-status {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
padding: 0.2rem 0.6rem;
|
align-items: center;
|
||||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
gap: 0.25rem;
|
||||||
color: #667eea;
|
padding: 0.25rem 0.75rem;
|
||||||
border-radius: 12px;
|
border-radius: 15px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-size: 0.85rem;
|
font-weight: 600;
|
||||||
white-space: nowrap;
|
}
|
||||||
|
|
||||||
|
.status-completed {
|
||||||
|
background: rgba(34, 197, 94, 0.1);
|
||||||
|
color: #16a34a;
|
||||||
|
border: 1px solid rgba(34, 197, 94, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-progress {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: #2563eb;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-projects {
|
.no-projects {
|
||||||
@@ -229,22 +349,56 @@
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-thumbnail {
|
.projects-grid {
|
||||||
height: 180px;
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-media {
|
||||||
|
height: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-title {
|
.project-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
min-height: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-content {
|
||||||
|
padding: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-footer {
|
||||||
|
padding: 0 0.85rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-left {
|
||||||
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-meta {
|
.project-meta {
|
||||||
font-size: 0.8rem;
|
font-size: 0.75rem;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.35rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
.project-thumbnail {
|
.projects-grid {
|
||||||
height: 160px;
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-media {
|
||||||
|
height: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-btn {
|
.category-btn {
|
||||||
@@ -255,6 +409,42 @@
|
|||||||
.project-content {
|
.project-content {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-footer {
|
||||||
|
padding: 0 0.75rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-title {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
min-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-description {
|
||||||
|
min-height: 2rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stats-section {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-left {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-info {
|
||||||
|
gap: 0.75rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-tag {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Скелетон загрузка */
|
/* Скелетон загрузка */
|
||||||
@@ -301,13 +491,22 @@
|
|||||||
{% for project in projects %}
|
{% for project in projects %}
|
||||||
<a href="{% url 'project_detail' project.pk %}" class="text-decoration-none">
|
<a href="{% url 'project_detail' project.pk %}" class="text-decoration-none">
|
||||||
<div class="project-card">
|
<div class="project-card">
|
||||||
<div class="project-thumbnail">
|
<!-- Верхняя часть - медиа контент -->
|
||||||
{% if project.thumbnail %}
|
<div class="project-media{% if not project.video and not project.thumbnail and not project.image %} no-image{% endif %}">
|
||||||
|
{% if project.video %}
|
||||||
|
<div class="media-player">
|
||||||
|
<video poster="{% if project.video_poster %}{{ project.video_poster.url }}{% elif project.thumbnail %}{{ project.thumbnail.url }}{% endif %}"
|
||||||
|
preload="metadata" muted>
|
||||||
|
<source src="{{ project.video.url }}" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
<button class="play-btn" type="button" aria-label="Воспроизвести видео">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% elif project.thumbnail %}
|
||||||
<img src="{{ project.thumbnail.url }}" alt="{{ project.name }}" loading="lazy" decoding="async">
|
<img src="{{ project.thumbnail.url }}" alt="{{ project.name }}" loading="lazy" decoding="async">
|
||||||
{% elif project.image %}
|
{% elif project.image %}
|
||||||
<img src="{{ project.image.url }}" alt="{{ project.name }}" loading="lazy" decoding="async">
|
<img src="{{ project.image.url }}" alt="{{ project.name }}" loading="lazy" decoding="async">
|
||||||
{% else %}
|
|
||||||
<img src="{% static 'img/default-project.jpg' %}" alt="{{ project.name }}" loading="lazy" decoding="async">
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if project.is_featured %}
|
{% if project.is_featured %}
|
||||||
@@ -317,31 +516,62 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Основной контент карточки -->
|
||||||
<div class="project-content">
|
<div class="project-content">
|
||||||
<h3 class="project-title">{{ project.name }}</h3>
|
<h3 class="project-title">{{ project.name }}</h3>
|
||||||
|
|
||||||
|
{% if project.short_description %}
|
||||||
|
<p class="project-description">{{ project.short_description|truncatewords:25 }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Статистика проекта -->
|
||||||
|
<div class="project-stats-section">
|
||||||
|
<div class="stats-left">
|
||||||
|
<div class="stat-item">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
<span>{{ project.views_count }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<i class="fas fa-heart"></i>
|
||||||
|
<span>{{ project.likes_count }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if project.completion_date %}
|
||||||
|
<div class="project-year">{{ project.completion_date|date:"Y" }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Футер карточки -->
|
||||||
|
<div class="project-footer">
|
||||||
{% if project.categories.exists %}
|
{% if project.categories.exists %}
|
||||||
<div class="project-categories">
|
<div class="project-categories">
|
||||||
{% for category in project.categories.all %}
|
{% for category in project.categories.all %}
|
||||||
<span class="category-tag">
|
<span class="category-tag">
|
||||||
<i class="{{ category.icon }} me-1"></i>{{ category.name }}
|
<i class="{{ category.icon }}"></i>{{ category.name }}
|
||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if project.short_description %}
|
|
||||||
<p class="project-description">{{ project.short_description|truncatewords:20 }}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="project-meta">
|
<div class="project-meta">
|
||||||
<div class="project-stats">
|
<div class="project-info">
|
||||||
<span><i class="fas fa-eye me-1"></i>{{ project.views_count }}</span>
|
{% if project.duration %}
|
||||||
<span><i class="fas fa-heart me-1"></i>{{ project.likes_count }}</span>
|
<span><i class="fas fa-clock"></i>{{ project.duration }}</span>
|
||||||
</div>
|
|
||||||
{% if project.completion_date %}
|
|
||||||
<small class="text-muted">{{ project.completion_date|date:"Y" }}</small>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if project.team_size %}
|
||||||
|
<span><i class="fas fa-users"></i>{{ project.team_size }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="project-status status-{{ project.status }}">
|
||||||
|
{% if project.status == 'completed' %}
|
||||||
|
<i class="fas fa-check-circle"></i>Завершен
|
||||||
|
{% elif project.status == 'in_progress' %}
|
||||||
|
<i class="fas fa-spinner"></i>В процессе
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-archive"></i>В архиве
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -360,3 +590,134 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Обработчик для видео плеера
|
||||||
|
const playBtns = document.querySelectorAll('.play-btn');
|
||||||
|
|
||||||
|
playBtns.forEach(btn => {
|
||||||
|
btn.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const mediaPlayer = this.closest('.media-player');
|
||||||
|
const video = mediaPlayer.querySelector('video');
|
||||||
|
|
||||||
|
if (video) {
|
||||||
|
if (video.paused) {
|
||||||
|
video.play();
|
||||||
|
this.style.opacity = '0';
|
||||||
|
} else {
|
||||||
|
video.pause();
|
||||||
|
this.style.opacity = '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработчики событий видео
|
||||||
|
const videos = document.querySelectorAll('.media-player video');
|
||||||
|
|
||||||
|
videos.forEach(video => {
|
||||||
|
const playBtn = video.closest('.media-player').querySelector('.play-btn');
|
||||||
|
|
||||||
|
video.addEventListener('play', () => {
|
||||||
|
if (playBtn) playBtn.style.opacity = '0';
|
||||||
|
});
|
||||||
|
|
||||||
|
video.addEventListener('pause', () => {
|
||||||
|
if (playBtn) playBtn.style.opacity = '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
video.addEventListener('ended', () => {
|
||||||
|
if (playBtn) playBtn.style.opacity = '1';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Анимация появления карточек при загрузке
|
||||||
|
const observerOptions = {
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px 0px -50px 0px'
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.style.opacity = '1';
|
||||||
|
entry.target.style.transform = 'translateY(0)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, observerOptions);
|
||||||
|
|
||||||
|
// Применяем анимацию к карточкам
|
||||||
|
const cards = document.querySelectorAll('.project-card');
|
||||||
|
cards.forEach((card, index) => {
|
||||||
|
card.style.opacity = '0';
|
||||||
|
card.style.transform = 'translateY(30px)';
|
||||||
|
card.style.transition = `opacity 0.6s ease ${index * 0.1}s, transform 0.6s ease ${index * 0.1}s`;
|
||||||
|
observer.observe(card);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lazy loading для изображений
|
||||||
|
const images = document.querySelectorAll('img[loading="lazy"]');
|
||||||
|
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
const imageObserver = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const img = entry.target;
|
||||||
|
img.classList.add('loaded');
|
||||||
|
imageObserver.unobserve(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
images.forEach(img => {
|
||||||
|
imageObserver.observe(img);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Анимации для загруженных изображений */
|
||||||
|
.project-media img {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-media img.loaded {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Плавная анимация при hover */
|
||||||
|
.project-card {
|
||||||
|
will-change: transform, box-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card:hover .project-media img {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Улучшенные переходы для статуса */
|
||||||
|
.status-completed {
|
||||||
|
background: rgba(34, 197, 94, 0.1);
|
||||||
|
color: #16a34a;
|
||||||
|
border: 1px solid rgba(34, 197, 94, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-in_progress {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: #2563eb;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-archived {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
color: #6b7280;
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ def about_view(request):
|
|||||||
|
|
||||||
|
|
||||||
def blog_view(request):
|
def blog_view(request):
|
||||||
blog_posts = BlogPost.objects.all().order_by('-created_at')
|
blog_posts = BlogPost.objects.all().order_by('-published_date')
|
||||||
return render(request, 'web/blog.html', {'blog_posts': blog_posts})
|
return render(request, 'web/blog.html', {'blog_posts': blog_posts})
|
||||||
|
|
||||||
def news_view(request):
|
def news_view(request):
|
||||||
|
|||||||
Reference in New Issue
Block a user