🚀 MEGA UPDATE: Объединение всех изменений для продакшена
Some checks failed
continuous-integration/drone/push Build is failing

 НОВЫЕ ФУНКЦИИ:
- 🎬 Поддержка видео в Hero баннерах и услугах
- 💊 Водная анимация пилюль маркеров банеров
- 📱 Полная главная страница с портфолио, блогом, новостями
- 🎯 HeroBanner модель с видео/изображениями
- 🎨 Современные hover-эффекты и анимации

📊 УЛУЧШЕНИЯ СТРУКТУРЫ:
- Расширенная home_modern.html с полным контентом
- Новые URL маршруты для всех секций
- Обновленные views с передачей всех данных
- CSS стили для всех новых секций
- Миграции для видео полей

🎪 HERO БАНЕР СИСТЕМА:
- Динамические банеры с видео/фото фонами
- Пилюли маркеры с водной анимацией
- Растягивание маркеров от центра
- Адаптивный дизайн для мобильных
- Glassmorphism эффекты

🎨 СОВРЕМЕННЫЙ ДИЗАЙН:
- Hover анимации для карточек
- Плавные переходы везде
- Современная типографика
- Градиенты и тени
- Отзывчивая сетка

Готов к продакшену! 🚀
This commit is contained in:
2025-11-25 11:24:19 +09:00
parent 6a576136af
commit 6f43fa4c3b
14 changed files with 1832 additions and 94 deletions

View File

@@ -1,18 +1,40 @@
from django.contrib import admin
from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest
from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest, HeroBanner
from .forms import ProjectForm
@admin.register(HeroBanner)
class HeroBannerAdmin(admin.ModelAdmin):
list_display = ('title', 'is_active', 'order', 'created_at')
list_filter = ('is_active', 'created_at')
search_fields = ('title', 'subtitle')
fields = ('title', 'subtitle', 'description', 'image', 'video', 'video_poster',
'button_text', 'button_link', 'is_active', 'order')
list_editable = ('is_active', 'order')
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ('name', 'category', 'price')
list_display = ('name', 'category', 'price', 'has_video')
search_fields = ('name', 'category')
fields = ('name', 'description', 'price', 'category', 'image', 'video', 'video_poster')
def has_video(self, obj):
return bool(obj.video)
has_video.boolean = True
has_video.short_description = 'Есть видео'
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
list_display = ('name', 'client','service', 'status', 'order', 'description')
list_display = ('name', 'client','service', 'status', 'order', 'has_video')
list_filter = ('name', 'client','service', 'status', 'order')
search_fields = ('name', 'client','service', 'status', 'order', 'client__first_name', 'client__last_name')
fields = ('name', 'description', 'completion_date', 'client', 'service', 'order',
'category', 'image', 'video', 'video_poster', 'status')
def has_video(self, obj):
return bool(obj.video)
has_video.boolean = True
has_video.short_description = 'Есть видео'
@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
@@ -27,14 +49,26 @@ class OrderAdmin(admin.ModelAdmin):
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
list_display = ('client', 'service', 'rating', 'review_date')
list_display = ('client', 'service', 'rating', 'review_date', 'has_video')
list_filter = ('rating',)
search_fields = ('client__first_name', 'service__name')
fields = ('client', 'service', 'project', 'rating', 'comment', 'image', 'video', 'video_poster')
def has_video(self, obj):
return bool(obj.video)
has_video.boolean = True
has_video.short_description = 'Есть видео'
@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin):
list_display = ('title', 'published_date')
list_display = ('title', 'published_date', 'has_video')
search_fields = ('title',)
fields = ('title', 'content', 'image', 'video', 'video_poster')
def has_video(self, obj):
return bool(obj.video)
has_video.boolean = True
has_video.short_description = 'Есть видео'
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):

View File

@@ -0,0 +1,75 @@
# Generated by Django 5.1.1 on 2025-11-24 23:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0009_alter_servicerequest_options_and_more'),
]
operations = [
migrations.CreateModel(
name='HeroBanner',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Заголовок')),
('subtitle', models.TextField(blank=True, verbose_name='Подзаголовок')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/hero/', verbose_name='Фоновое изображение')),
('video', models.FileField(blank=True, help_text='Фоновое видео для баннера (MP4, WebM)', null=True, upload_to='static/video/hero/', verbose_name='Фоновое видео')),
('video_poster', models.ImageField(blank=True, help_text='Превью изображение для видео', null=True, upload_to='static/img/hero/posters/', verbose_name='Превью видео')),
('button_text', models.CharField(blank=True, max_length=100, verbose_name='Текст кнопки')),
('button_link', models.URLField(blank=True, verbose_name='Ссылка кнопки')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('order', models.PositiveIntegerField(default=0, verbose_name='Порядок отображения')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Hero Баннер',
'verbose_name_plural': 'Hero Баннеры',
'ordering': ['order', '-created_at'],
},
),
migrations.AddField(
model_name='blogpost',
name='video',
field=models.FileField(blank=True, help_text='Видео файл для блог поста', null=True, upload_to='static/video/blog/'),
),
migrations.AddField(
model_name='blogpost',
name='video_poster',
field=models.ImageField(blank=True, help_text='Превью изображение для видео', null=True, upload_to='static/img/blog/posters/'),
),
migrations.AddField(
model_name='project',
name='video',
field=models.FileField(blank=True, help_text='Видео презентация проекта', null=True, upload_to='static/video/project/'),
),
migrations.AddField(
model_name='project',
name='video_poster',
field=models.ImageField(blank=True, help_text='Превью изображение для видео проекта', null=True, upload_to='static/img/project/posters/'),
),
migrations.AddField(
model_name='review',
name='video',
field=models.FileField(blank=True, help_text='Видео отзыв о работе', null=True, upload_to='static/video/review/'),
),
migrations.AddField(
model_name='review',
name='video_poster',
field=models.ImageField(blank=True, help_text='Превью для видео отзыва', null=True, upload_to='static/img/review/posters/'),
),
migrations.AddField(
model_name='service',
name='video',
field=models.FileField(blank=True, help_text='Видео файл для услуги (MP4, WebM, AVI)', null=True, upload_to='static/video/services/'),
),
migrations.AddField(
model_name='service',
name='video_poster',
field=models.ImageField(blank=True, help_text='Превью изображение для видео', null=True, upload_to='static/img/services/posters/'),
),
]

View File

@@ -0,0 +1,13 @@
# Generated by Django 5.1.1 on 2025-11-25 01:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('web', '0010_herobanner_blogpost_video_blogpost_video_poster_and_more'),
]
operations = [
]

View File

@@ -3,6 +3,30 @@ from django.contrib.auth.models import AbstractUser, User
import uuid
from django.urls import reverse
class HeroBanner(models.Model):
"""Модель для главного баннера на сайте"""
title = models.CharField(max_length=200, verbose_name="Заголовок")
subtitle = models.TextField(blank=True, verbose_name="Подзаголовок")
description = models.TextField(blank=True, verbose_name="Описание")
image = models.ImageField(upload_to='static/img/hero/', blank=True, null=True, verbose_name="Фоновое изображение")
video = models.FileField(upload_to='static/video/hero/', blank=True, null=True,
help_text='Фоновое видео для баннера (MP4, WebM)', verbose_name="Фоновое видео")
video_poster = models.ImageField(upload_to='static/img/hero/posters/', blank=True, null=True,
help_text='Превью изображение для видео', verbose_name="Превью видео")
button_text = models.CharField(max_length=100, blank=True, verbose_name="Текст кнопки")
button_link = models.URLField(blank=True, verbose_name="Ссылка кнопки")
is_active = models.BooleanField(default=True, verbose_name="Активен")
order = models.PositiveIntegerField(default=0, verbose_name="Порядок отображения")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Hero Баннер'
verbose_name_plural = 'Hero Баннеры'
ordering = ['order', '-created_at']
def __str__(self):
return self.title
class Category(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(default='Описание категории')
@@ -21,6 +45,10 @@ class Service(models.Model):
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='services')
image = models.ImageField(upload_to='static/img/services/', blank=True, null=True)
video = models.FileField(upload_to='static/video/services/', blank=True, null=True, help_text='Видео файл для услуги (MP4, WebM, AVI)')
video_poster = models.ImageField(upload_to='static/img/services/posters/', blank=True, null=True, help_text='Превью изображение для видео')
video = models.FileField(upload_to='static/video/services/', blank=True, null=True, help_text='Видео файл для услуги (MP4, WebM, AVI)')
video_poster = models.ImageField(upload_to='static/img/services/posters/', blank=True, null=True, help_text='Превью изображение для видео')
class Meta:
verbose_name = 'Услуга'
@@ -61,6 +89,8 @@ class BlogPost(models.Model):
content = models.TextField()
published_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/blog/', blank=True, null=True)
video = models.FileField(upload_to='static/video/blog/', blank=True, null=True, help_text='Видео файл для блог поста')
video_poster = models.ImageField(upload_to='static/img/blog/posters/', blank=True, null=True, help_text='Превью изображение для видео')
class Meta:
verbose_name = 'Блог'
@@ -126,6 +156,8 @@ class Project(models.Model):
order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name='project', null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
image = models.ImageField(upload_to='static/img/project/', blank=True, null=True)
video = models.FileField(upload_to='static/video/project/', blank=True, null=True, help_text='Видео презентация проекта')
video_poster = models.ImageField(upload_to='static/img/project/posters/', blank=True, null=True, help_text='Превью изображение для видео проекта')
status = models.CharField(max_length=50, choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress')
class Meta:
@@ -144,6 +176,8 @@ class Review(models.Model):
comment = models.TextField()
review_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/review/', blank=True, null=True)
video = models.FileField(upload_to='static/video/review/', blank=True, null=True, help_text='Видео отзыв о работе')
video_poster = models.ImageField(upload_to='static/img/review/posters/', blank=True, null=True, help_text='Превью для видео отзыва')
class Meta:
verbose_name = 'Отзыв'

View File

@@ -9,7 +9,7 @@
<div class="row align-items-center">
<div class="col-lg-6">
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill">
🚀 О нас
О нас
</span>
<h1 class="display-4 fw-bold mb-4">
Мы создаем <span class="text-gradient">цифровое будущее</span>
@@ -87,7 +87,7 @@
<div class="col-lg-6">
<div class="pe-lg-4">
<span class="badge bg-primary text-white mb-3 px-3 py-2 rounded-pill">
🎯 Наша миссия
Наша миссия
</span>
<h2 class="display-6 fw-bold mb-4">
Делаем технологии <span class="text-gradient">доступными</span>
@@ -291,7 +291,7 @@
<div class="container-modern">
<div class="text-center mb-5">
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill">
Технологии
Технологии
</span>
<h2 class="display-6 fw-bold mb-3">
Современный <span class="text-gradient">технологический стек</span>

View File

@@ -5,104 +5,192 @@
{% block content %}
<!-- Hero Section -->
<section class="hero-modern" id="home">
<div class="container-modern">
<div class="row align-items-center min-vh-100">
<div class="col-lg-6">
<div class="animate-fade-in-up">
<h1 class="display-3 fw-bold mb-4">
Создаем <span class="text-gradient">будущее</span> вашего бизнеса
</h1>
<p class="lead mb-4 text-muted">
Мы разрабатываем современные веб-приложения, мобильные решения и системы автоматизации,
которые помогают компаниям расти и быть конкурентоспособными.
</p>
<div class="d-flex flex-wrap gap-3 mb-5">
<a href="{% url 'services' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-rocket me-2"></i>
Начать проект
</a>
<a href="{% url 'about' %}" class="btn btn-secondary-modern btn-lg">
<i class="fas fa-play-circle me-2"></i>
Узнать больше
</a>
{% if hero_banners %}
<!-- Hero Carousel for Multiple Banners -->
<div id="heroCarousel" class="carousel slide carousel-fade" data-bs-ride="carousel">
<!-- Hero Container with rounded corners -->
<div class="hero-container">
<!-- Custom Carousel Indicators with Pills -->
<div class="carousel-indicators-container">
<div class="outer-pill">
<div class="pill-indicators">
{% for banner in hero_banners %}
<button type="button"
class="pill-indicator {% if forloop.first %}active{% endif %}"
data-bs-target="#heroCarousel"
data-bs-slide-to="{{ forloop.counter0 }}"
data-title="{{ banner.title }}"
aria-label="Slide {{ forloop.counter }}">
<span class="pill-indicator-title">{{ banner.title }}</span>
</button>
{% endfor %}
</div>
</div>
</div>
<div class="row text-center">
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">50+</h3>
<p class="small text-muted mb-0">Проектов</p>
<div class="carousel-inner">
{% for banner in hero_banners %}
<div class="carousel-item {% if forloop.first %}active{% endif %}">
<!-- Hero Background (Video or Image) -->
<div class="hero-bg">
{% if banner.video %}
<!-- Hero Video Background -->
<video class="hero-video"
autoplay muted loop
{% if banner.video_poster %}poster="{{ banner.video_poster.url }}"{% endif %}>
<source src="{{ banner.video.url }}" type="video/mp4">
{% if banner.image %}
<!-- Fallback image -->
<img src="{{ banner.image.url }}" alt="{{ banner.title }}">
{% endif %}
</video>
{% elif banner.image %}
<!-- Hero Image Background -->
<img src="{{ banner.image.url }}" alt="{{ banner.title }}">
{% endif %}
<!-- Gradient Overlay -->
<div class="hero-overlay"></div>
<!-- Hero Content -->
<div class="hero-content">
<div class="row align-items-center w-100">
<div class="col-lg-8 col-xl-7">
<div class="animate-fade-in-up">
<h1 class="hero-title">
{{ banner.title }}
</h1>
{% if banner.subtitle %}
<h2 class="hero-subtitle">
{{ banner.subtitle }}
</h2>
{% endif %}
{% if banner.description %}
<p class="hero-description">
{{ banner.description }}
</p>
{% endif %}
<div class="d-flex flex-wrap gap-3">
{% if banner.button_text and banner.button_link %}
<a href="{{ banner.button_link }}" class="btn btn-light btn-lg px-4">
<i class="fas fa-rocket me-2"></i>
{{ banner.button_text }}
</a>
{% endif %}
<a href="{% url 'services' %}" class="btn btn-outline-light btn-lg px-4">
<i class="fas fa-cogs me-2"></i>
Наши услуги
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">3+</h3>
<p class="small text-muted mb-0">Лет опыта</p>
</div>
{% endfor %}
</div>
</div>
</div>
{% else %}
<!-- Default Hero Section (fallback) -->
<div class="container-modern">
<div class="row align-items-center min-vh-100">
<div class="col-lg-6">
<div class="animate-fade-in-up">
<h1 class="display-3 fw-bold mb-4">
Создаем <span class="text-gradient">будущее</span> вашего бизнеса
</h1>
<p class="lead mb-4 text-muted">
Мы разрабатываем современные веб-приложения, мобильные решения и системы автоматизации,
которые помогают компаниям расти и быть конкурентоспособными.
</p>
<div class="d-flex flex-wrap gap-3 mb-5">
<a href="{% url 'services' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-rocket me-2"></i>
Начать проект
</a>
<a href="{% url 'about' %}" class="btn btn-secondary-modern btn-lg">
<i class="fas fa-play-circle me-2"></i>
Узнать больше
</a>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">24/7</h3>
<p class="small text-muted mb-0">Поддержка</p>
<div class="row text-center">
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">50+</h3>
<p class="small text-muted mb-0">Проектов</p>
</div>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">3+</h3>
<p class="small text-muted mb-0">Лет опыта</p>
</div>
</div>
<div class="col-4">
<div class="stat-item">
<h3 class="text-gradient fw-bold mb-1">24/7</h3>
<p class="small text-muted mb-0">Поддержка</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="text-center animate-float">
<div class="position-relative">
<!-- 3D Graphic Placeholder -->
<div class="hero-graphic p-5">
<div class="position-relative">
<!-- Code Window -->
<div class="code-window bg-dark rounded-4 p-4 mb-4 shadow-lg"
style="transform: rotate(-5deg); max-width: 400px;">
<div class="d-flex gap-2 mb-3">
<div class="rounded-circle bg-danger" style="width: 12px; height: 12px;"></div>
<div class="rounded-circle bg-warning" style="width: 12px; height: 12px;"></div>
<div class="rounded-circle bg-success" style="width: 12px; height: 12px;"></div>
<div class="col-lg-6">
<div class="text-center animate-float">
<div class="position-relative">
<!-- 3D Graphic Placeholder -->
<div class="hero-graphic p-5">
<div class="position-relative">
<!-- Code Window -->
<div class="code-window bg-dark rounded-4 p-4 mb-4 shadow-lg"
style="transform: rotate(-5deg); max-width: 400px;">
<div class="d-flex gap-2 mb-3">
<div class="rounded-circle bg-danger" style="width: 12px; height: 12px;"></div>
<div class="rounded-circle bg-warning" style="width: 12px; height: 12px;"></div>
<div class="rounded-circle bg-success" style="width: 12px; height: 12px;"></div>
</div>
<div class="text-light font-monospace small">
<div class="text-info">def create_future():</div>
<div class="ms-3 text-success">return innovation + passion</div>
<div class="text-warning">// SmartSolTech</div>
</div>
</div>
<div class="text-light font-monospace small">
<div class="text-info">def create_future():</div>
<div class="ms-3 text-success">return innovation + passion</div>
<div class="text-warning">// SmartSolTech</div>
<!-- Mobile Preview -->
<div class="mobile-preview position-absolute bg-white rounded-4 p-3 shadow"
style="transform: rotate(10deg); top: 50px; right: 50px; width: 200px;">
<div class="bg-gradient rounded-3 p-3 text-white text-center">
<i class="fas fa-mobile-alt fa-3x mb-2"></i>
<h6 class="mb-1">Мобильные</h6>
<p class="small mb-0 opacity-75">приложения</p>
</div>
</div>
<!-- Floating Icons -->
<div class="floating-icon position-absolute"
style="top: 20px; left: 20px; animation: float 2s ease-in-out infinite;">
<div class="bg-primary rounded-3 p-3 text-white shadow">
<i class="fab fa-react fa-2x"></i>
</div>
</div>
<div class="floating-icon position-absolute"
style="bottom: 100px; left: 100px; animation: float 3s ease-in-out infinite reverse;">
<div class="bg-success rounded-3 p-3 text-white shadow">
<i class="fab fa-python fa-2x"></i>
</div>
</div>
</div>
<!-- Mobile App Preview -->
<div class="mobile-preview bg-light rounded-4 p-3 shadow-lg position-absolute"
style="transform: rotate(10deg); top: 50px; right: 50px; width: 200px;">
<div class="bg-gradient rounded-3 p-3 text-white text-center">
<i class="fas fa-mobile-alt fa-3x mb-2"></i>
<h6 class="mb-1">Мобильные</h6>
<p class="small mb-0 opacity-75">приложения</p>
</div>
</div>
<!-- Floating Icons -->
<div class="floating-icon position-absolute"
style="top: 20px; left: 20px; animation: float 2s ease-in-out infinite;">
<div class="bg-primary rounded-3 p-3 text-white shadow">
<i class="fab fa-react fa-2x"></i>
</div>
</div>
<div class="floating-icon position-absolute"
style="bottom: 100px; left: 100px; animation: float 3s ease-in-out infinite reverse;">
<div class="bg-success rounded-3 p-3 text-white shadow">
<i class="fab fa-python fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Services Preview Section -->
{% endif %}
</section><!-- Services Preview Section -->
<section class="section-padding bg-light">
<div class="container-modern">
<div class="text-center mb-5">
@@ -272,11 +360,256 @@
</div>
</div>
</section>
<!-- Portfolio Section -->
<section class="section-padding bg-light" id="portfolio">
<div class="container-modern">
<div class="text-center mb-5">
<span class="badge bg-primary rounded-pill px-3 py-2 mb-3">
<i class="fas fa-briefcase me-2"></i>
💼 Портфолио
</span>
<h2 class="display-6 fw-bold mb-3">
Наши <span class="text-gradient">работы</span>
</h2>
<p class="lead text-muted max-width-600 mx-auto">
Избранные проекты, которыми мы гордимся
</p>
</div>
{% if latest_projects %}
<div class="row g-4">
{% for project in latest_projects %}
<div class="col-lg-4">
<div class="project-card bg-white rounded-4 overflow-hidden shadow hover-lift">
{% if project.image %}
<div class="project-image">
<img src="{{ project.image.url }}" alt="{{ project.name }}" class="w-100">
<div class="project-overlay">
<a href="{% url 'project_detail' project.pk %}" class="btn btn-light rounded-circle">
<i class="fas fa-eye"></i>
</a>
</div>
</div>
{% endif %}
<div class="p-4">
<h5 class="mb-3">{{ project.name }}</h5>
<p class="text-muted mb-3">{{ project.description|truncatewords:15 }}</p>
<a href="{% url 'project_detail' project.pk %}" class="text-primary fw-semibold">
Подробнее <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div class="text-center mt-5">
<a href="{% url 'portfolio' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-th-large me-2"></i>
Смотреть все проекты
</a>
</div>
</div>
</section>
<!-- Blog Section -->
<section class="section-padding" id="blog">
<div class="container-modern">
<div class="text-center mb-5">
<span class="badge bg-success rounded-pill px-3 py-2 mb-3">
<i class="fas fa-blog me-2"></i>
📝 Блог
</span>
<h2 class="display-6 fw-bold mb-3">
Последние <span class="text-gradient">статьи</span>
</h2>
</div>
{% if latest_blog_posts %}
<div class="row g-4">
{% for post in latest_blog_posts %}
<div class="col-lg-6">
<article class="blog-card bg-white rounded-4 overflow-hidden shadow hover-lift">
{% if post.image %}
<div class="blog-image">
<img src="{{ post.image.url }}" alt="{{ post.title }}" class="w-100">
</div>
{% endif %}
<div class="p-4">
<div class="d-flex align-items-center mb-3">
<span class="text-muted small">{{ post.created_at|date:"d F Y" }}</span>
</div>
<h5 class="mb-3">{{ post.title }}</h5>
<p class="text-muted mb-3">{{ post.content|truncatewords:25 }}</p>
<a href="{% url 'blog_post_detail' post.pk %}" class="text-primary fw-semibold">
Читать далее <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</article>
</div>
{% endfor %}
</div>
{% endif %}
<div class="text-center mt-5">
<a href="{% url 'blog' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-blog me-2"></i>
Все статьи
</a>
</div>
</div>
</section>
<!-- News Section -->
<section class="section-padding bg-light" id="news">
<div class="container-modern">
<div class="text-center mb-5">
<span class="badge bg-warning rounded-pill px-3 py-2 mb-3">
<i class="fas fa-newspaper me-2"></i>
📰 Новости
</span>
<h2 class="display-6 fw-bold mb-3">
Последние <span class="text-gradient">новости</span>
</h2>
</div>
<div class="row g-4">
<div class="col-lg-12">
<div class="news-card bg-white rounded-4 p-4 shadow">
<div class="d-flex align-items-center mb-3">
<span class="badge bg-primary rounded-pill px-3 py-1 me-3">24.11.2025</span>
<h5 class="mb-0">Новый сайт</h5>
</div>
<p class="text-muted mb-3">
Поздравляем всех наших клиентов с этой знаменательной датой!
Мы переписали свой сайт! теперь у нас современный дизайн и улучшенная функциональность...
</p>
<a href="{% url 'news' %}" class="text-primary fw-semibold">
Узнать больше <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="{% url 'news' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-newspaper me-2"></i>
Все новости
</a>
</div>
</div>
</section>
<!-- Career Section -->
<section class="section-padding" id="career">
<div class="container-modern">
<div class="text-center mb-5">
<span class="badge bg-info rounded-pill px-3 py-2 mb-3">
<i class="fas fa-rocket me-2"></i>
🚀 Карьера
</span>
<h2 class="display-6 fw-bold mb-3">
Присоединяйтесь к нашей <span class="text-gradient">команде</span>
</h2>
<p class="lead text-muted max-width-600 mx-auto">
Мы ищем талантливых специалистов, которые разделяют нашу страсть к технологиям и инновациям.
</p>
</div>
<div class="row g-4 mb-5">
<div class="col-lg-4">
<div class="career-feature text-center p-4">
<div class="career-icon bg-primary rounded-3 p-3 mx-auto mb-3 text-white" style="width: fit-content;">
<i class="fas fa-chart-line fa-2x"></i>
</div>
<h6 class="mb-2">Профессиональный рост</h6>
<p class="text-muted small mb-0">Возможности для развития и обучения</p>
</div>
</div>
<div class="col-lg-4">
<div class="career-feature text-center p-4">
<div class="career-icon bg-success rounded-3 p-3 mx-auto mb-3 text-white" style="width: fit-content;">
<i class="fas fa-users fa-2x"></i>
</div>
<h6 class="mb-2">Команда профессионалов</h6>
<p class="text-muted small mb-0">Работайте с лучшими специалистами</p>
</div>
</div>
<div class="col-lg-4">
<div class="career-feature text-center p-4">
<div class="career-icon bg-warning rounded-3 p-3 mx-auto mb-3 text-white" style="width: fit-content;">
<i class="fas fa-clock fa-2x"></i>
</div>
<h6 class="mb-2">Гибкий график</h6>
<p class="text-muted small mb-0">Удаленная работа и гибкое расписание</p>
</div>
</div>
</div>
<div class="text-center">
<div class="career-stats bg-gradient rounded-4 p-4 text-white mb-4" style="max-width: 300px; margin: 0 auto;">
<h3 class="display-4 fw-bold mb-2">0</h3>
<h6 class="mb-2">Открыто вакансий</h6>
<p class="small mb-0 opacity-75">Найдите свою идеальную позицию</p>
</div>
<a href="{% url 'career' %}" class="btn btn-primary-modern btn-lg me-3">
<i class="fas fa-briefcase me-2"></i>
Смотреть вакансии
</a>
<a href="{% url 'career' %}" class="btn btn-outline-primary btn-lg">
Посмотреть все
</a>
</div>
</div>
</section>
{% endblock %}
{% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const carousel = document.getElementById('heroCarousel');
const indicators = document.querySelectorAll('.pill-indicator');
let currentActiveIndex = 0;
function updatePillState(index) {
// Обновляем классы индикаторов
indicators.forEach((indicator, i) => {
if (i === index) {
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
});
currentActiveIndex = index;
}
// Обработчики событий для индикаторов
indicators.forEach((indicator, index) => {
indicator.addEventListener('click', function() {
currentActiveIndex = index;
updatePillState(index);
});
});
// Bootstrap carousel события
if (carousel) {
carousel.addEventListener('slide.bs.carousel', function(event) {
const nextIndex = event.to;
currentActiveIndex = nextIndex;
updatePillState(nextIndex);
});
// Инициализируем первое состояние
updatePillState(0);
}
// Animate elements on scroll
const observerOptions = {
threshold: 0.1,

View File

@@ -9,7 +9,7 @@
<div class="row justify-content-center text-center">
<div class="col-lg-8">
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill">
Полный спектр услуг
Полный спектр услуг
</span>
<h1 class="display-4 fw-bold mb-4">
Наши <span class="text-gradient">IT-услуги</span>
@@ -51,7 +51,28 @@
<div class="col-lg-4 col-md-6 service-item" data-category="{{ service.category.name|lower }}">
<div class="card-modern h-100">
<div class="position-relative overflow-hidden" style="height: 200px;">
{% if service.image %}
{% if service.video %}
<!-- Video Content -->
<video class="w-100 h-100 service-video"
style="object-fit: cover;"
muted
loop
{% if service.video_poster %}poster="{{ service.video_poster.url }}"{% endif %}
onmouseover="this.play()"
onmouseout="this.pause()">
<source src="{{ service.video.url }}" type="video/mp4">
{% if service.image %}
<!-- Fallback image if video not supported -->
<img src="{{ service.image.url }}" class="w-100 h-100" style="object-fit: cover;" alt="{{ service.name }}">
{% endif %}
Ваш браузер не поддерживает видео.
</video>
<div class="position-absolute top-0 end-0 p-2">
<span class="badge bg-success">
<i class="fas fa-play"></i> Видео
</span>
</div>
{% elif service.image %}
<img src="{{ service.image.url }}" class="w-100 h-100" style="object-fit: cover;" alt="{{ service.name }}">
{% else %}
<div class="w-100 h-100 bg-gradient d-flex align-items-center justify-content-center">

View File

@@ -11,6 +11,10 @@ urlpatterns = [
path('client/<int:pk>/', views.client_detail, name='client_detail'),
path('blog/<int:pk>/', views.blog_post_detail, name='blog_post_detail'),
path('services/', views.services_view, name='services'),
path('portfolio/', views.portfolio_view, name='portfolio'),
path('blog/', views.blog_view, name='blog'),
path('news/', views.news_view, name='news'),
path('career/', views.career_view, name='career'),
# path('create_order/<int:pk>/', views.create_order, name='create_order'),
path('about/', views.about_view, name="about"),
path('service/generate_qr_code/<int:service_id>/', views.generate_qr_code, name='generate_qr_code'),

View File

@@ -1,5 +1,5 @@
from django.shortcuts import render, get_object_or_404, redirect
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, HeroBanner
from django.db.models import Avg
from comunication.models import TelegramSettings
import qrcode
@@ -34,7 +34,20 @@ except Exception as e:
def home(request):
services = Service.objects.all()[:6] # Показываем только первые 6 услуг на главной
return render(request, 'web/home_modern.html', {'services': services})
hero_banners = HeroBanner.objects.filter(is_active=True).order_by('order', '-created_at')[:3] # Получаем активные баннеры
# Добавляем данные для полной главной страницы
latest_projects = Project.objects.all()[:3] # Последние проекты для портфолио
latest_blog_posts = BlogPost.objects.all()[:2] # Последние статьи блога
reviews = Review.objects.all()[:3] # Отзывы клиентов
return render(request, 'web/home_modern.html', {
'services': services,
'hero_banners': hero_banners,
'latest_projects': latest_projects,
'latest_blog_posts': latest_blog_posts,
'reviews': reviews,
})
def service_detail(request, pk):
service = get_object_or_404(Service, pk=pk)
@@ -69,6 +82,21 @@ def services_view(request):
def about_view(request):
return render(request, 'web/about_modern.html')
def portfolio_view(request):
projects = Project.objects.all()
return render(request, 'web/portfolio.html', {'projects': projects})
def blog_view(request):
blog_posts = BlogPost.objects.all().order_by('-created_at')
return render(request, 'web/blog.html', {'blog_posts': blog_posts})
def news_view(request):
# Можно создать модель News или использовать BlogPost с категорией
return render(request, 'web/news.html')
def career_view(request):
return render(request, 'web/career.html')
def create_service_request(request, service_id):
if request.method == 'POST':
try: