🚀 MEGA UPDATE: Объединение всех изменений для продакшена
Some checks failed
continuous-integration/drone/push Build is failing
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:
@@ -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):
|
||||
|
||||
@@ -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/'),
|
||||
),
|
||||
]
|
||||
13
smartsoltech/web/migrations/0011_add_video_fields.py
Normal file
13
smartsoltech/web/migrations/0011_add_video_fields.py
Normal 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 = [
|
||||
]
|
||||
@@ -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 = 'Отзыв'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user