init commit

This commit is contained in:
2025-11-24 07:02:33 +09:00
commit 7bf003e70d
488 changed files with 51130 additions and 0 deletions

View File

48
smartsoltech/web/admin.py Normal file
View File

@@ -0,0 +1,48 @@
from django.contrib import admin
from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest
from .forms import ProjectForm
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ('name', 'category', 'price')
search_fields = ('name', 'category')
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
list_display = ('name', 'client','service', 'status', 'order', 'description')
list_filter = ('name', 'client','service', 'status', 'order')
search_fields = ('name', 'client','service', 'status', 'order', 'client__first_name', 'client__last_name')
@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email', 'phone_number')
search_fields = ('first_name', 'last_name', 'email')
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'service', 'client', 'client__email', 'client__phone_number', 'status')
list_filter = ('status','client', 'order_date')
search_fields = ('client__first_name', 'service__name','status','client', 'order_date')
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
list_display = ('client', 'service', 'rating', 'review_date')
list_filter = ('rating',)
search_fields = ('client__first_name', 'service__name')
@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin):
list_display = ('title', 'published_date')
search_fields = ('title',)
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name','description')
search_fields = ('name',)
@admin.register(ServiceRequest)
class ServiceRequestAdmin(admin.ModelAdmin):
list_display = ('service','token', 'client', 'created_at')
search_fields = ('service','token', 'client')
list_filter = ('service','token','client')

13
smartsoltech/web/apps.py Normal file
View File

@@ -0,0 +1,13 @@
from django.apps import AppConfig
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = 'web'
def ready(self):
import web.signals
class WebConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'web'

10
smartsoltech/web/forms.py Normal file
View File

@@ -0,0 +1,10 @@
from django import forms
from .models import Order, Project
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['order'].queryset = Order.objects.filter(status='completed', project__isnull=True)

View File

@@ -0,0 +1,115 @@
# Generated by Django 5.1.1 on 2024-10-08 13:56
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='BlogPost',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('content', models.TextField()),
('published_date', models.DateTimeField(auto_now_add=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/blog/')),
],
),
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(default='Описание категории')),
],
),
migrations.CreateModel(
name='Client',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100)),
('last_name', models.CharField(max_length=100)),
('email', models.EmailField(max_length=254, unique=True)),
('phone_number', models.CharField(max_length=15, unique=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/customer/')),
],
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.TextField(blank=True, null=True)),
('order_date', models.DateTimeField(auto_now_add=True)),
('status', models.CharField(choices=[('pending', 'Pending'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=50)),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.client')),
],
),
migrations.CreateModel(
name='Project',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField(default='Описание проекта')),
('completion_date', models.DateField(blank=True, null=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/project/')),
('status', models.CharField(choices=[('in_progress', 'In Progress'), ('completed', 'Completed')], default='in_progress', max_length=50)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='web.category')),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.client')),
('order', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project', to='web.order')),
],
),
migrations.CreateModel(
name='Service',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField(default='Описание услуги')),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/services/')),
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='services', to='web.category')),
],
),
migrations.CreateModel(
name='Review',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField()),
('comment', models.TextField()),
('review_date', models.DateTimeField(auto_now_add=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/review/')),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.client')),
('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.project')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.service')),
],
),
migrations.AddField(
model_name='project',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.service'),
),
migrations.AddField(
model_name='order',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.service'),
),
migrations.CreateModel(
name='ServiceRequest',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('client_name', models.CharField(max_length=100)),
('client_email', models.EmailField(max_length=254)),
('client_phone', models.CharField(max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('token', models.UUIDField(default=uuid.uuid4, unique=True)),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.service')),
],
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.1.1 on 2024-10-13 03:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='client',
name='chat_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='servicerequest',
name='chat_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.1.1 on 2024-10-13 03:54
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0002_client_chat_id_servicerequest_chat_id'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='client',
name='user',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='client_profile', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.1.1 on 2024-10-13 03:56
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0003_client_user'),
]
operations = [
migrations.AlterModelOptions(
name='order',
options={'ordering': ['-order_date'], 'verbose_name': 'Заказ', 'verbose_name_plural': 'Заказы'},
),
migrations.AlterField(
model_name='order',
name='client',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_orders', to='web.client'),
),
migrations.AlterField(
model_name='order',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_orders', to='web.service'),
),
]

View File

@@ -0,0 +1,51 @@
# Generated by Django 5.1.1 on 2024-10-13 04:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0004_alter_order_options_alter_order_client_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='blogpost',
options={'ordering': ['-published_date'], 'verbose_name': 'Блог', 'verbose_name_plural': 'Блоги'},
),
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name'], 'verbose_name': 'Категория', 'verbose_name_plural': 'Категории'},
),
migrations.AlterModelOptions(
name='client',
options={'ordering': ['last_name', 'first_name'], 'verbose_name': 'Клиент', 'verbose_name_plural': 'Клиенты'},
),
migrations.AlterModelOptions(
name='project',
options={'ordering': ['-completion_date'], 'verbose_name': 'Проект', 'verbose_name_plural': 'Проекты'},
),
migrations.AlterModelOptions(
name='review',
options={'ordering': ['-review_date'], 'verbose_name': 'Отзыв', 'verbose_name_plural': 'Отзывы'},
),
migrations.AlterModelOptions(
name='service',
options={'ordering': ['name'], 'verbose_name': 'Услуга', 'verbose_name_plural': 'Услуги'},
),
migrations.AlterModelOptions(
name='servicerequest',
options={'ordering': ['-created_at'], 'verbose_name': 'Заявка на услугу', 'verbose_name_plural': 'Заявки на услуги'},
),
migrations.AlterField(
model_name='order',
name='status',
field=models.CharField(choices=[('pending', 'Ожидание'), ('in_progress', 'В процессе'), ('completed', 'Завершен'), ('cancelled', 'Отменён')], default='pending', max_length=50),
),
migrations.AlterField(
model_name='project',
name='status',
field=models.CharField(choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress', max_length=50),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.1.1 on 2024-10-14 11:10
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0005_alter_blogpost_options_alter_category_options_and_more'),
]
operations = [
migrations.AddField(
model_name='order',
name='service_request',
field=models.OneToOneField(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='related_order', to='web.servicerequest'),
preserve_default=False,
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.1.1 on 2024-10-14 11:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0006_order_service_request'),
]
operations = [
migrations.RemoveField(
model_name='servicerequest',
name='client_email',
),
migrations.RemoveField(
model_name='servicerequest',
name='client_name',
),
migrations.RemoveField(
model_name='servicerequest',
name='client_phone',
),
migrations.AddField(
model_name='servicerequest',
name='client',
field=models.ForeignKey(default=12, on_delete=django.db.models.deletion.CASCADE, related_name='related_service_requests', to='web.client'),
preserve_default=False,
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.1 on 2024-10-14 11:48
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0007_remove_servicerequest_client_email_and_more'),
]
operations = [
migrations.AlterField(
model_name='servicerequest',
name='client',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='related_service_requests', to='web.client'),
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.1.1 on 2024-11-01 04:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0008_alter_servicerequest_client'),
]
operations = [
migrations.AlterModelOptions(
name='servicerequest',
options={'ordering': ['-is_verified', '-created_at'], 'verbose_name': 'Заявка на услугу', 'verbose_name_plural': 'Заявки на услуги'},
),
migrations.AddField(
model_name='servicerequest',
name='is_verified',
field=models.BooleanField(default=False),
),
]

View File

155
smartsoltech/web/models.py Normal file
View File

@@ -0,0 +1,155 @@
from django.db import models
from django.contrib.auth.models import AbstractUser, User
import uuid
from django.urls import reverse
class Category(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(default='Описание категории')
class Meta:
verbose_name = 'Категория'
verbose_name_plural = 'Категории'
ordering = ['name']
def __str__(self):
return self.name
class Service(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(default='Описание услуги')
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)
class Meta:
verbose_name = 'Услуга'
verbose_name_plural = 'Услуги'
ordering = ['name']
def __str__(self):
return self.name
def average_rating(self):
reviews = self.reviews.all()
if reviews:
return sum(review.rating for review in reviews) / reviews.count()
return 0
def review_count(self):
return self.reviews.count()
class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='client_profile', null=True, blank=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
phone_number = models.CharField(max_length=15, unique=True)
image = models.ImageField(upload_to='static/img/customer/', blank=True, null=True)
chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID
class Meta:
verbose_name = 'Клиент'
verbose_name_plural = 'Клиенты'
ordering = ['last_name', 'first_name']
def __str__(self):
return f"{self.first_name} {self.last_name} {self.chat_id}"
class BlogPost(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/blog/', blank=True, null=True)
class Meta:
verbose_name = 'Блог'
verbose_name_plural = 'Блоги'
ordering = ['-published_date']
def __str__(self):
return self.title
class ServiceRequest(models.Model):
service = models.ForeignKey(Service, on_delete=models.CASCADE)
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_service_requests', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена
chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID
is_verified = models.BooleanField(default=False)
class Meta:
verbose_name = 'Заявка на услугу'
verbose_name_plural = 'Заявки на услуги'
ordering = ['-is_verified', '-created_at']
def __str__(self):
return f"Request for {self.service.name} by {self.client.first_name}"
class Order(models.Model):
service_request = models.OneToOneField(ServiceRequest, on_delete=models.CASCADE, related_name='related_order')
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_orders')
service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='related_orders')
message = models.TextField(blank=True, null=True)
order_date = models.DateTimeField(auto_now_add=True)
status = models.CharField(
max_length=50,
choices=[
('pending', 'Ожидание'),
('in_progress', 'В процессе'),
('completed', 'Завершен'),
('cancelled', 'Отменён')
],
default='pending'
)
class Meta:
ordering = ['-order_date']
verbose_name = 'Заказ'
verbose_name_plural = 'Заказы'
def __str__(self):
return f"Order #{self.id} by {self.client.first_name}"
def is_completed(self):
return self.status == 'completed'
def get_absolute_url(self):
return reverse('order_detail', kwargs={'pk': self.pk})
class Project(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(default='Описание проекта')
completion_date = models.DateField(blank=True, null=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects')
service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='projects')
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)
status = models.CharField(max_length=50, choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress')
class Meta:
verbose_name = 'Проект'
verbose_name_plural = 'Проекты'
ordering = ['-completion_date']
def __str__(self):
return self.name
class Review(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='reviews')
service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='reviews')
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='reviews', blank=True, null=True)
rating = models.IntegerField()
comment = models.TextField()
review_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/review/', blank=True, null=True)
class Meta:
verbose_name = 'Отзыв'
verbose_name_plural = 'Отзывы'
ordering = ['-review_date']
def __str__(self):
return f"Отзыв от {self.client.first_name} {self.client.last_name} for {self.service.name}"

View File

@@ -0,0 +1,22 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Order, Project
@receiver(post_save, sender=Order)
def create_project_on_order_completed(sender, instance, **kwargs):
if instance.status == 'completed' and not Project.objects.filter(order=instance).exists():
Project.objects.create(
name=f"Project for {instance.service.name}",
description=instance.message,
client=instance.client,
service=instance.service,
order=instance,
category=instance.service.category,
status='in_progress'
)
@receiver(post_save, sender=Project)
def prompt_review_on_project_completion(sender, instance, **kwargs):
if instance.status == 'completed':
# Logic to prompt the client for a review (e.g., sending an email or notification)
pass

0
smartsoltech/web/templates/.gitignore vendored Normal file
View File

View File

@@ -0,0 +1,53 @@
{% extends 'web/base.html' %}
{% load static %}
{% block content %}
<section class="position-relative py-4 py-xl-5">
<div class="container position-relative">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Contact us</h2>
<p class="w-lg-50"></p>
</div>
</div>
<div class="row d-flex justify-content-center">
<div class="col-md-6 col-lg-4 col-xl-4">
<div class="d-flex flex-column justify-content-center align-items-start h-100">
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-rounded bs-icon-primary d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon"><svg class="bi bi-telephone" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.568 17.568 0 0 0 4.168 6.608 17.569 17.569 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.678.678 0 0 0-.58-.122l-2.19.547a1.745 1.745 0 0 1-1.657-.459L5.482 8.062a1.745 1.745 0 0 1-.46-1.657l.548-2.19a.678.678 0 0 0-.122-.58L3.654 1.328zM1.884.511a1.745 1.745 0 0 1 2.612.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.678.678 0 0 0 .178.643l2.457 2.457a.678.678 0 0 0 .644.178l2.189-.547a1.745 1.745 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.634 18.634 0 0 1-7.01-4.42 18.634 18.634 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877L1.885.511z"></path>
</svg></div>
<div class="px-2">
<h6 class="mb-0">Телефон</h6>
<p class="mb-0"><a href ="tel:01056936103">010-5693-6103</a></p>
</div>
</div>
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-rounded bs-icon-primary d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon"><svg class="bi bi-envelope" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"></path>
</svg></div>
<div class="px-2">
<h6 class="mb-0">Email</h6>
<p class="mb-0"><a href="mailto:a.choi@smartsoltech.kr">a.choi@smartsoltech.kr</a></p>
</div>
</div>
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-rounded bs-icon-primary d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon"><svg class="bi bi-pin" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M4.146.146A.5.5 0 0 1 4.5 0h7a.5.5 0 0 1 .5.5c0 .68-.342 1.174-.646 1.479-.126.125-.25.224-.354.298v4.431l.078.048c.203.127.476.314.751.555C12.36 7.775 13 8.527 13 9.5a.5.5 0 0 1-.5.5h-4v4.5c0 .276-.224 1.5-.5 1.5s-.5-1.224-.5-1.5V10h-4a.5.5 0 0 1-.5-.5c0-.973.64-1.725 1.17-2.189A5.921 5.921 0 0 1 5 6.708V2.277a2.77 2.77 0 0 1-.354-.298C4.342 1.674 4 1.179 4 .5a.5.5 0 0 1 .146-.354zm1.58 1.408-.002-.001.002.001m-.002-.001.002.001A.5.5 0 0 1 6 2v5a.5.5 0 0 1-.276.447h-.002l-.012.007-.054.03a4.922 4.922 0 0 0-.827.58c-.318.278-.585.596-.725.936h7.792c-.14-.34-.407-.658-.725-.936a4.915 4.915 0 0 0-.881-.61l-.012-.006h-.002A.5.5 0 0 1 10 7V2a.5.5 0 0 1 .295-.458 1.775 1.775 0 0 0 .351-.271c.08-.08.155-.17.214-.271H5.14c.06.1.133.191.214.271a1.78 1.78 0 0 0 .37.282"></path>
</svg></div>
<div class="px-2">
<h6 class="mb-0">Мы находимся:</h6>
<p class="mb-0">Чолланамдо, Кванджу</p>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-5 col-xl-4">
<div></div>
<h1>О нас</h1>
<p>Мы - команда профессионалов в своей отрасли. В нашей команде есть все, программисты, сетевые инженеры, системные администраторы, </p>
</div>
</div>
</div>
</section>
{% endblock %}

View File

@@ -0,0 +1,556 @@
{% 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 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>
</h1>
<p class="lead text-muted mb-4">
SmartSolTech - это команда профессионалов, которые превращают идеи в инновационные IT-решения.
Мы помогаем бизнесу расти и развиваться в цифровую эпоху.
</p>
<div class="d-flex flex-wrap gap-3">
<a href="#team" class="btn btn-primary-modern">
<i class="fas fa-users me-2"></i>
Наша команда
</a>
<a href="#contact" class="btn btn-secondary-modern">
<i class="fas fa-envelope me-2"></i>
Связаться с нами
</a>
</div>
</div>
<div class="col-lg-6">
<div class="text-center">
<div class="position-relative">
<!-- Company illustration -->
<div class="about-graphic">
<div class="row g-3">
<div class="col-6">
<div class="stat-card bg-white rounded-4 p-4 shadow animate-float">
<div class="stat-icon bg-primary rounded-3 mb-3 mx-auto" style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-code text-white fa-2x"></i>
</div>
<h3 class="text-gradient fw-bold">50+</h3>
<p class="mb-0 text-muted">Проектов</p>
</div>
</div>
<div class="col-6">
<div class="stat-card bg-white rounded-4 p-4 shadow animate-float" style="animation-delay: 0.5s;">
<div class="stat-icon bg-success rounded-3 mb-3 mx-auto" style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-users text-white fa-2x"></i>
</div>
<h3 class="text-gradient fw-bold">30+</h3>
<p class="mb-0 text-muted">Клиентов</p>
</div>
</div>
<div class="col-6">
<div class="stat-card bg-white rounded-4 p-4 shadow animate-float" style="animation-delay: 1s;">
<div class="stat-icon bg-warning rounded-3 mb-3 mx-auto" style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-award text-white fa-2x"></i>
</div>
<h3 class="text-gradient fw-bold">3+</h3>
<p class="mb-0 text-muted">Лет опыта</p>
</div>
</div>
<div class="col-6">
<div class="stat-card bg-white rounded-4 p-4 shadow animate-float" style="animation-delay: 1.5s;">
<div class="stat-icon bg-info rounded-3 mb-3 mx-auto" style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-rocket text-white fa-2x"></i>
</div>
<h3 class="text-gradient fw-bold">24/7</h3>
<p class="mb-0 text-muted">Поддержка</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Mission & Vision -->
<section class="section-padding bg-light">
<div class="container-modern">
<div class="row g-5">
<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>
</h2>
<p class="text-muted mb-4">
Мы верим, что каждый бизнес заслуживает доступа к современным технологиям.
Наша миссия — демократизировать IT-решения и помочь компаниям любого размера
достичь цифрового совершенства.
</p>
<div class="mission-points">
<div class="d-flex align-items-start mb-3">
<i class="fas fa-check-circle text-success me-3 mt-1"></i>
<div>
<h6>Инновационные решения</h6>
<p class="text-muted mb-0 small">Используем передовые технологии для создания уникальных продуктов</p>
</div>
</div>
<div class="d-flex align-items-start mb-3">
<i class="fas fa-check-circle text-success me-3 mt-1"></i>
<div>
<h6>Клиентоориентированность</h6>
<p class="text-muted mb-0 small">Фокусируемся на потребностях и целях каждого клиента</p>
</div>
</div>
<div class="d-flex align-items-start">
<i class="fas fa-check-circle text-success me-3 mt-1"></i>
<div>
<h6>Непрерывное развитие</h6>
<p class="text-muted mb-0 small">Постоянно совершенствуем наши навыки и знания</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="ps-lg-4">
<span class="badge bg-secondary text-white mb-3 px-3 py-2 rounded-pill">
🔮 Наше видение
</span>
<h2 class="display-6 fw-bold mb-4">
Будущее начинается <span class="text-gradient">сегодня</span>
</h2>
<p class="text-muted mb-4">
Мы стремимся стать ведущей IT-компанией в Корее, известной своими инновационными решениями,
высоким качеством сервиса и способностью трансформировать бизнес-идеи в успешные цифровые продукты.
</p>
<!-- Progress bars -->
<div class="skills-progress">
<div class="skill-item mb-3">
<div class="d-flex justify-content-between mb-2">
<span class="fw-semibold">Веб-разработка</span>
<span class="text-muted">95%</span>
</div>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-gradient" role="progressbar" style="width: 95%"></div>
</div>
</div>
<div class="skill-item mb-3">
<div class="d-flex justify-content-between mb-2">
<span class="fw-semibold">Мобильная разработка</span>
<span class="text-muted">90%</span>
</div>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-gradient" role="progressbar" style="width: 90%"></div>
</div>
</div>
<div class="skill-item mb-3">
<div class="d-flex justify-content-between mb-2">
<span class="fw-semibold">UI/UX Дизайн</span>
<span class="text-muted">85%</span>
</div>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-gradient" role="progressbar" style="width: 85%"></div>
</div>
</div>
<div class="skill-item">
<div class="d-flex justify-content-between mb-2">
<span class="fw-semibold">DevOps</span>
<span class="text-muted">80%</span>
</div>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-gradient" role="progressbar" style="width: 80%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Team Section -->
<section class="section-padding" id="team">
<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-5 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">
<!-- Team Member 1 -->
<div class="col-lg-4 col-md-6">
<div class="card-modern text-center h-100">
<div class="position-relative">
<div class="team-avatar mx-auto mb-3" style="width: 120px; height: 120px; background: var(--gradient-primary); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-user text-white fa-3x"></i>
</div>
</div>
<div class="card-body">
<h5 class="mb-2">Алексей Чой</h5>
<p class="text-primary mb-3">CEO & Founder</p>
<p class="text-muted small mb-3">
Визионер и лидер команды с более чем 5-летним опытом в IT-индустрии.
Специализируется на стратегическом планировании и управлении проектами.
</p>
<div class="d-flex justify-content-center gap-2">
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-linkedin-in"></i>
</a>
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-github"></i>
</a>
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-telegram-plane"></i>
</a>
</div>
</div>
</div>
</div>
<!-- Team Member 2 -->
<div class="col-lg-4 col-md-6">
<div class="card-modern text-center h-100">
<div class="position-relative">
<div class="team-avatar mx-auto mb-3" style="width: 120px; height: 120px; background: var(--gradient-accent); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-user text-white fa-3x"></i>
</div>
</div>
<div class="card-body">
<h5 class="mb-2">Анна Ким</h5>
<p class="text-success mb-3">Lead Developer</p>
<p class="text-muted small mb-3">
Опытный full-stack разработчик со страстью к созданию масштабируемых
и эффективных веб-приложений. Эксперт в React, Django и cloud технологиях.
</p>
<div class="d-flex justify-content-center gap-2">
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-linkedin-in"></i>
</a>
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-github"></i>
</a>
</div>
</div>
</div>
</div>
<!-- Team Member 3 -->
<div class="col-lg-4 col-md-6">
<div class="card-modern text-center h-100">
<div class="position-relative">
<div class="team-avatar mx-auto mb-3" style="width: 120px; height: 120px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-user text-white fa-3x"></i>
</div>
</div>
<div class="card-body">
<h5 class="mb-2">Дмитрий Пак</h5>
<p class="text-warning mb-3">UI/UX Designer</p>
<p class="text-muted small mb-3">
Креативный дизайнер, создающий интуитивные и привлекательные пользовательские интерфейсы.
Специализируется на UX-исследованиях и современном веб-дизайне.
</p>
<div class="d-flex justify-content-center gap-2">
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-dribbble"></i>
</a>
<a href="#" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
<i class="fab fa-behance"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Technologies Section -->
<section class="section-padding bg-light">
<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>
</h2>
<p class="lead text-muted">
Мы используем проверенные и инновационные технологии для создания качественных решений
</p>
</div>
<div class="row g-4">
<!-- Frontend -->
<div class="col-lg-3 col-md-6">
<div class="text-center">
<h5 class="mb-3">Frontend</h5>
<div class="d-flex flex-wrap justify-content-center gap-3">
<div class="tech-icon">
<i class="fab fa-react text-info fa-2x"></i>
<small class="d-block mt-1">React</small>
</div>
<div class="tech-icon">
<i class="fab fa-vue text-success fa-2x"></i>
<small class="d-block mt-1">Vue.js</small>
</div>
<div class="tech-icon">
<i class="fab fa-html5 text-danger fa-2x"></i>
<small class="d-block mt-1">HTML5</small>
</div>
<div class="tech-icon">
<i class="fab fa-css3-alt text-primary fa-2x"></i>
<small class="d-block mt-1">CSS3</small>
</div>
</div>
</div>
</div>
<!-- Backend -->
<div class="col-lg-3 col-md-6">
<div class="text-center">
<h5 class="mb-3">Backend</h5>
<div class="d-flex flex-wrap justify-content-center gap-3">
<div class="tech-icon">
<i class="fab fa-python text-warning fa-2x"></i>
<small class="d-block mt-1">Python</small>
</div>
<div class="tech-icon">
<i class="fas fa-server text-success fa-2x"></i>
<small class="d-block mt-1">Django</small>
</div>
<div class="tech-icon">
<i class="fab fa-node-js text-success fa-2x"></i>
<small class="d-block mt-1">Node.js</small>
</div>
<div class="tech-icon">
<i class="fas fa-database text-info fa-2x"></i>
<small class="d-block mt-1">PostgreSQL</small>
</div>
</div>
</div>
</div>
<!-- Mobile -->
<div class="col-lg-3 col-md-6">
<div class="text-center">
<h5 class="mb-3">Mobile</h5>
<div class="d-flex flex-wrap justify-content-center gap-3">
<div class="tech-icon">
<i class="fab fa-react text-info fa-2x"></i>
<small class="d-block mt-1">React Native</small>
</div>
<div class="tech-icon">
<i class="fab fa-android text-success fa-2x"></i>
<small class="d-block mt-1">Android</small>
</div>
<div class="tech-icon">
<i class="fab fa-apple text-dark fa-2x"></i>
<small class="d-block mt-1">iOS</small>
</div>
<div class="tech-icon">
<i class="fas fa-mobile-alt text-primary fa-2x"></i>
<small class="d-block mt-1">Flutter</small>
</div>
</div>
</div>
</div>
<!-- DevOps -->
<div class="col-lg-3 col-md-6">
<div class="text-center">
<h5 class="mb-3">DevOps</h5>
<div class="d-flex flex-wrap justify-content-center gap-3">
<div class="tech-icon">
<i class="fab fa-docker text-primary fa-2x"></i>
<small class="d-block mt-1">Docker</small>
</div>
<div class="tech-icon">
<i class="fab fa-aws text-warning fa-2x"></i>
<small class="d-block mt-1">AWS</small>
</div>
<div class="tech-icon">
<i class="fab fa-github text-dark fa-2x"></i>
<small class="d-block mt-1">GitHub</small>
</div>
<div class="tech-icon">
<i class="fas fa-cogs text-secondary fa-2x"></i>
<small class="d-block mt-1">CI/CD</small>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section class="section-padding bg-gradient text-white" id="contact">
<div class="container-modern">
<div class="text-center mb-5">
<h2 class="display-6 fw-bold mb-4">
Начнем сотрудничество?
</h2>
<p class="lead opacity-90 mb-5">
Свяжитесь с нами для обсуждения вашего проекта
</p>
</div>
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="row g-4">
<div class="col-md-4 text-center">
<div class="contact-item">
<i class="fas fa-envelope fa-2x mb-3 opacity-75"></i>
<h5>Email</h5>
<a href="mailto:info@smartsoltech.kr" class="text-white text-decoration-none opacity-75">
info@smartsoltech.kr
</a>
</div>
</div>
<div class="col-md-4 text-center">
<div class="contact-item">
<i class="fas fa-phone fa-2x mb-3 opacity-75"></i>
<h5>Телефон</h5>
<a href="tel:+82-10-XXXX-XXXX" class="text-white text-decoration-none opacity-75">
+82-10-XXXX-XXXX
</a>
</div>
</div>
<div class="col-md-4 text-center">
<div class="contact-item">
<i class="fab fa-telegram-plane fa-2x mb-3 opacity-75"></i>
<h5>Telegram</h5>
<a href="https://t.me/smartsoltech" class="text-white text-decoration-none opacity-75">
@smartsoltech
</a>
</div>
</div>
</div>
<div class="text-center mt-5">
<a href="{% url 'services' %}" class="btn btn-light btn-lg text-primary">
<i class="fas fa-rocket me-2"></i>
Начать проект
</a>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Animate progress bars when they come into view
const observerOptions = {
threshold: 0.5
};
const progressObserver = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const progressBars = entry.target.querySelectorAll('.progress-bar');
progressBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = width;
}, 200);
});
}
});
}, observerOptions);
const skillsSection = document.querySelector('.skills-progress');
if (skillsSection) {
progressObserver.observe(skillsSection);
}
// Animate stat cards
const statCards = document.querySelectorAll('.stat-card');
statCards.forEach((card, index) => {
setTimeout(() => {
card.classList.add('animate-fade-in-up');
}, index * 200);
});
});
</script>
<style>
.max-width-600 {
max-width: 600px;
}
.tech-icon {
padding: 1rem;
border-radius: 12px;
background: var(--bg-light);
box-shadow: var(--shadow);
transition: all 0.3s ease;
}
.tech-icon:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.progress-bar {
transition: width 1.5s ease-in-out;
}
.contact-item {
padding: 2rem 1rem;
}
.contact-item:hover i {
transform: scale(1.1);
transition: transform 0.3s ease;
}
.team-avatar {
transition: all 0.3s ease;
}
.card-modern:hover .team-avatar {
transform: scale(1.05);
}
@media (max-width: 768px) {
.about-graphic .row {
justify-content: center;
}
.stat-card {
margin-bottom: 1rem;
}
.tech-icon {
margin: 0.25rem;
padding: 0.75rem;
}
}
</style>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% load static %}
<!-- web/templates/web/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="manifest" href="/static/manifest.json">
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<link rel="stylesheet" href="{% static 'assets/css/modal-styles.css' %}">
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<title>{% block title %}Smartsoltech{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'assets/css/styles.min.css' %}">
</head>
<body>
{% include 'web/navbar.html' %}
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
{% include 'web/footer.html' %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,72 @@
{% load static %}
<!DOCTYPE html>
<html lang="ru" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="SmartSolTech - Современные IT-решения для вашего бизнеса">
<meta name="keywords" content="IT, разработка, веб-дизайн, мобильные приложения, SmartSolTech">
<meta name="author" content="SmartSolTech">
<!-- Preconnect to Google Fonts for performance -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom Styles -->
<link rel="stylesheet" href="{% static 'assets/css/modern-styles.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/modal-styles.css' %}">
<!-- Manifest -->
<link rel="manifest" href="/static/manifest.json">
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' fill='%236366f1'/><text y='70' font-size='60' fill='white' font-family='Arial,sans-serif' text-anchor='middle' x='50'>S</text></svg>">>
<title>{% block title %}SmartSolTech - Современные IT-решения{% endblock %}</title>
{% block extra_head %}{% endblock %}
</head>
<body>
<!-- Loading Screen -->
<div id="loading-screen" class="position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center" style="background: var(--bg-light); z-index: 9999;">
<div class="loading-spinner"></div>
</div>
<!-- Navigation -->
{% include 'web/navbar_modern.html' %}
<!-- Main Content -->
<main>
{% block content %}{% endblock %}
</main>
<!-- Footer -->
{% include 'web/footer_modern.html' %}
<!-- Theme Toggle Button -->
<button id="theme-toggle" class="theme-toggle" aria-label="Переключить тему">
<i class="fas fa-moon"></i>
</button>
<!-- Scroll to Top Button -->
<button id="scroll-to-top" class="position-fixed bottom-0 end-0 m-4 btn btn-primary-modern rounded-circle" style="width: 50px; height: 50px; display: none; z-index: 999;">
<i class="fas fa-arrow-up"></i>
</button>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="{% static 'assets/js/modern-scripts.js' %}"></script>
<script src="{% static 'assets/js/modal-init.js' %}"></script>
{% block extra_scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,13 @@
{% extends 'web/base.html' %}
{% block content %}
<h2>Мои заказы</h2>
<ul>
{% for order in orders %}
<li>
<strong>Заказ №{{ order.id }}:</strong> {{ order.service.name }} - Статус: {{ order.get_status_display }}
<a href="{% url 'order_detail' order.pk %}">Подробнее</a>
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -0,0 +1,29 @@
{% extends 'web/base.html' %}
<!-- web/templates/web/complete_registration_basic.html -->
{% load static %}
{% include 'web/header.html' %}
{% block content %}
<div class="container-fluid">
<h1>Регистрация</h1>
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="client_name">Имя</label>
<input type="text" id="client_name" name="client_name" required>
</div>
<div class="form-group">
<label for="client_email">Email</label>
<input type="email" id="client_email" name="client_email" required>
</div>
<div class="form-group">
<label for="client_phone">Телефон</label>
<input type="tel" id="client_phone" name="client_phone" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
</div>
<!-- Подключение JavaScript файлов из static -->
<script src="{% static 'assets/js/script.min.js' %}"></script>
{% endblock%}

View File

@@ -0,0 +1,29 @@
{% extends 'web/base.html' %}
<!-- web/templates/web/complete_registration_basic.html -->
{% load static %}
{% include 'web/header.html' %}
{% block content %}
<div class="container-fluid">
<h1>Регистрация</h1>
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="client_name">Имя</label>
<input type="text" id="client_name" name="client_name" required>
</div>
<div class="form-group">
<label for="client_email">Email</label>
<input type="email" id="client_email" name="client_email" required>
</div>
<div class="form-group">
<label for="client_phone">Телефон</label>
<input type="tel" id="client_phone" name="client_phone" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
</div>
<!-- Подключение JavaScript файлов из static -->
<script src="{% static 'assets/js/script.min.js' %}"></script>
{% endblock%}

View File

@@ -0,0 +1,48 @@
<!-- web/templates/web/footer.html -->
{% load static %}
<footer class="text-light bg-dark pt-5 pb-4">
<div class="container text-md-left">
<div class="row text-md-left">
<div class="col-md-3 col-lg-3 col-xl-3 mx-auto mt-3">
<h5 class="text-uppercase text-warning mb-4 font-weight-bold">SmartSolTech</h5>
<p>Future begins here...</p>
</div>
<div class="col-md-2 col-lg-2 col-xl-2 mx-auto mt-3">
<h5 class="text-uppercase text-warning mb-4 font-weight-bold">Products</h5>
<p><a class="text-light" href="#" style="text-decoration: none;">Product 1</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Product 2</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Product 3</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Product 4</a></p>
</div>
<div class="col-md-3 col-lg-2 col-xl-2 mx-auto mt-3">
<h5 class="text-uppercase text-warning mb-4 font-weight-bold">Usefu links:</h5>
<p><a class="text-light" href="#" style="text-decoration: none;">Your Account</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Become an Affiliate</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Shipping Rates</a></p>
<p><a class="text-light" href="#" style="text-decoration: none;">Help</a></p>
</div>
<div class="col-md-4 col-lg-3 col-xl-3 mx-auto mt-3">
<h5 class="text-uppercase text-warning mb-4 font-weight-bold">Contact</h5>
<p><i class="fas fa-home mr-3"></i> Gwangju-si Cheollanam-do, Republic of Korea</p>
<p><i class="fas fa-envelope mr-3"></i> a.choi@smartsoltech.kr</p>
<p><i class="fas fa-phone mr-3"></i> + 8 (210) 5693 6103</p>
</div>
</div>
<hr class="mb-4" />
<div class="row align-items-center">
<div class="col-md-7 col-lg-8">
<p class="text-md-left">© 2024 SmartSolTech. All rights reserved.</p>
</div>
<div class="col-md-5 col-lg-4">
<div class="text-md-right">
<ul class="list-unstyled list-inline">
<li class="list-inline-item"><a class="btn-floating btn-sm text-light" href="#" style="font-size: 23px;"><i class="fab fa-facebook"></i></a></li>
<li class="list-inline-item"><a class="btn-floating btn-sm text-light" href="#" style="font-size: 23px;"><i class="fab fa-twitter"></i></a></li>
<li class="list-inline-item"><a class="btn-floating btn-sm text-light" href="#" style="font-size: 23px;"><i class="fab fa-google-plus"></i></a></li>
<li class="list-inline-item"><a class="btn-floating btn-sm text-light" href="#" style="font-size: 23px;"><i class="fab fa-linkedin-in"></i></a></li>
</ul>
</div>
</div>
</div>
</div>
</footer>

View File

@@ -0,0 +1,189 @@
{% load static %}
<footer class="bg-dark text-light section-padding mt-5">
<div class="container-modern">
<div class="row g-4">
<!-- Company Info -->
<div class="col-lg-4 col-md-6">
<div class="mb-4">
<h4 class="text-gradient mb-3">
<i class="fas fa-code me-2"></i>
SmartSolTech
</h4>
<p class="text-light opacity-75 mb-4">
Мы создаем инновационные IT-решения, которые помогают бизнесу расти и развиваться в цифровую эпоху.
</p>
<div class="d-flex gap-3">
<a href="#" class="btn btn-outline-light rounded-circle" style="width: 45px; height: 45px;">
<i class="fab fa-telegram-plane"></i>
</a>
<a href="#" class="btn btn-outline-light rounded-circle" style="width: 45px; height: 45px;">
<i class="fab fa-instagram"></i>
</a>
<a href="#" class="btn btn-outline-light rounded-circle" style="width: 45px; height: 45px;">
<i class="fab fa-linkedin-in"></i>
</a>
<a href="#" class="btn btn-outline-light rounded-circle" style="width: 45px; height: 45px;">
<i class="fab fa-github"></i>
</a>
</div>
</div>
</div>
<!-- Services -->
<div class="col-lg-2 col-md-6">
<h5 class="mb-3 text-light">Услуги</h5>
<ul class="list-unstyled">
<li class="mb-2">
<a href="{% url 'services' %}" class="text-light opacity-75 text-decoration-none hover-primary">
Веб-разработка
</a>
</li>
<li class="mb-2">
<a href="{% url 'services' %}" class="text-light opacity-75 text-decoration-none hover-primary">
Мобильные приложения
</a>
</li>
<li class="mb-2">
<a href="{% url 'services' %}" class="text-light opacity-75 text-decoration-none hover-primary">
UI/UX Дизайн
</a>
</li>
<li class="mb-2">
<a href="{% url 'services' %}" class="text-light opacity-75 text-decoration-none hover-primary">
DevOps
</a>
</li>
</ul>
</div>
<!-- Company -->
<div class="col-lg-2 col-md-6">
<h5 class="mb-3 text-light">Компания</h5>
<ul class="list-unstyled">
<li class="mb-2">
<a href="{% url 'about' %}" class="text-light opacity-75 text-decoration-none hover-primary">
О нас
</a>
</li>
<li class="mb-2">
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary">
Портфолио
</a>
</li>
<li class="mb-2">
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary">
Команда
</a>
</li>
<li class="mb-2">
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary">
Карьера
</a>
</li>
</ul>
</div>
<!-- Contact Info -->
<div class="col-lg-4 col-md-6">
<h5 class="mb-3 text-light">Контакты</h5>
<div class="mb-3">
<div class="d-flex align-items-center mb-2">
<i class="fas fa-envelope me-3 text-primary"></i>
<a href="mailto:info@smartsoltech.kr" class="text-light opacity-75 text-decoration-none hover-primary">
info@smartsoltech.kr
</a>
</div>
<div class="d-flex align-items-center mb-2">
<i class="fas fa-phone me-3 text-primary"></i>
<a href="tel:+82-10-XXXX-XXXX" class="text-light opacity-75 text-decoration-none hover-primary">
+82-10-XXXX-XXXX
</a>
</div>
<div class="d-flex align-items-start mb-2">
<i class="fas fa-map-marker-alt me-3 text-primary mt-1"></i>
<span class="text-light opacity-75">
Seoul, South Korea
</span>
</div>
</div>
<!-- Newsletter -->
<div class="mt-4">
<h6 class="text-light mb-2">Подписаться на новости</h6>
<form class="d-flex">
<input type="email" class="form-control me-2 bg-transparent border-light text-light"
placeholder="Ваш email" style="border-radius: 10px;">
<button type="submit" class="btn btn-primary-modern">
<i class="fas fa-paper-plane"></i>
</button>
</form>
</div>
</div>
</div>
<hr class="my-5 border-light opacity-25">
<!-- Copyright -->
<div class="row align-items-center">
<div class="col-md-6">
<p class="mb-0 text-light opacity-75">
© 2025 SmartSolTech. Все права защищены.
</p>
</div>
<div class="col-md-6">
<div class="d-md-flex justify-content-md-end">
<ul class="list-inline mb-0">
<li class="list-inline-item">
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary small">
Политика конфиденциальности
</a>
</li>
<li class="list-inline-item ms-3">
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary small">
Условия использования
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
<style>
.hover-primary {
transition: all 0.3s ease;
}
.hover-primary:hover {
color: var(--primary-color) !important;
opacity: 1 !important;
}
footer {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%) !important;
position: relative;
}
footer::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="dots" width="10" height="10" patternUnits="userSpaceOnUse"><circle cx="5" cy="5" r="0.5" fill="%236366f1" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23dots)"/></svg>');
opacity: 0.3;
}
footer .container-modern {
position: relative;
z-index: 1;
}
.btn-outline-light:hover {
background: var(--gradient-primary) !important;
border-color: transparent !important;
transform: translateY(-2px);
}
</style>

View File

@@ -0,0 +1,20 @@
<!-- web/templates/web/header.html -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'home' %}">Smartsoltech</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'home' %}">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'services' %}">Услуги</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'about' %}">Контакты</a>
</li>
</ul>
</div>
</nav>

View File

@@ -0,0 +1,35 @@
<!-- web/templates/web/home.html -->
{% extends 'web/base.html' %}
{% load static %}
{% block title %}Главная{% endblock %}
{% block content %}
<div id="carousel-1" class="carousel slide" data-bs-ride="carousel" style="height: 600px;">
<div class="carousel-inner h-100">
{% for service in services %}
<div class="carousel-item {% if forloop.first %}active{% endif %} h-100">
<div class="carousel-overlay"></div>
<img class="w-100 d-block position-absolute h-100 fit-cover" src="{{ service.image.url }}" alt="{{ service.name }}" style="z-index: -1; filter: brightness(0.5);" />
<div class="container d-flex flex-column justify-content-center h-100 position-relative" style="z-index: 2;">
<div class="row">
<div class="col-md-6 col-xl-4 offset-md-2">
<div style="max-width: 350px;">
<h1 class="text-uppercase fw-bold text-white">{{ service.name }}</h1>
<p class="my-3 text-white">{{ service.description }}</p>
<a class="btn btn-primary btn-lg me-2" role="button" href="{% url 'service_detail' service.pk %}">Подробнее</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div><a class="carousel-control-prev" href="#carousel-1" role="button" data-bs-slide="prev"><span class="carousel-control-prev-icon"></span><span class="visually-hidden">Previous</span></a><a class="carousel-control-next" href="#carousel-1" role="button" data-bs-slide="next"><span class="carousel-control-next-icon"></span><span class="visually-hidden">Next</span></a></div>
<div class="carousel-indicators">
{% for service in services %}
<button class="{% if forloop.first %}active{% endif %}" type="button" data-bs-target="#carousel-1" data-bs-slide-to="{{ forloop.counter0 }}"></button>
{% endfor %}
</div>
</div>
<br><br><br>
{% endblock %}

View File

@@ -0,0 +1,370 @@
{% extends 'web/base_modern.html' %}
{% load static %}
{% block title %}SmartSolTech - Современные IT-решения для вашего бизнеса{% endblock %}
{% 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">
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill fs-6">
🚀 Инновационные IT-решения
</span>
<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="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 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>
<!-- 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 -->
<section class="section-padding bg-light">
<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-5 fw-bold mb-3">
Полный спектр <span class="text-gradient">IT-услуг</span>
</h2>
<p class="lead text-muted max-width-600 mx-auto">
От идеи до реализации - мы предоставляем комплексные решения для вашего цифрового успеха
</p>
</div>
<div class="services-grid">
{% for service in services %}
<div class="service-card">
<div class="service-icon">
<i class="fas fa-{% cycle 'code' 'mobile-alt' 'paint-brush' 'server' 'chart-line' 'shield-alt' %}"></i>
</div>
<h4 class="mb-3">{{ service.name }}</h4>
<p class="text-muted mb-4">{{ service.description|truncatewords:20 }}</p>
<a href="{% url 'service_detail' service.pk %}" class="btn btn-outline-primary">
Подробнее <i class="fas fa-arrow-right ms-2"></i>
</a>
</div>
{% endfor %}
</div>
<div class="text-center mt-5">
<a href="{% url 'services' %}" class="btn btn-primary-modern btn-lg">
<i class="fas fa-th-large me-2"></i>
Все услуги
</a>
</div>
</div>
</section>
<!-- Why Choose Us Section -->
<section class="section-padding">
<div class="container-modern">
<div class="row align-items-center">
<div class="col-lg-6">
<div class="pe-lg-5">
<span class="badge bg-gradient text-white mb-3 px-3 py-2 rounded-pill">
🎯 Почему мы
</span>
<h2 class="display-6 fw-bold mb-4">
Ваш надежный <span class="text-gradient">IT-партнер</span>
</h2>
<p class="text-muted mb-4">
Мы не просто выполняем проекты - мы создаем долгосрочные партнерские отношения
и помогаем бизнесу расти с помощью технологий.
</p>
<div class="feature-list">
<div class="d-flex align-items-start mb-4">
<div class="feature-icon bg-primary rounded-3 p-2 me-3 text-white">
<i class="fas fa-rocket"></i>
</div>
<div>
<h5 class="mb-2">Быстрая разработка</h5>
<p class="text-muted mb-0">Agile-методология и современные инструменты для быстрой доставки результата</p>
</div>
</div>
<div class="d-flex align-items-start mb-4">
<div class="feature-icon bg-success rounded-3 p-2 me-3 text-white">
<i class="fas fa-shield-alt"></i>
</div>
<div>
<h5 class="mb-2">Высокое качество</h5>
<p class="text-muted mb-0">Тщательное тестирование и code review обеспечивают надежность решений</p>
</div>
</div>
<div class="d-flex align-items-start mb-4">
<div class="feature-icon bg-warning rounded-3 p-2 me-3 text-white">
<i class="fas fa-headset"></i>
</div>
<div>
<h5 class="mb-2">24/7 Поддержка</h5>
<p class="text-muted mb-0">Постоянная техническая поддержка и сопровождение проектов</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="position-relative">
<!-- Process Steps -->
<div class="process-steps">
<div class="step-card active bg-white rounded-4 p-4 shadow mb-4">
<div class="d-flex align-items-center">
<div class="step-number bg-primary text-white rounded-circle me-3"
style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
1
</div>
<div>
<h6 class="mb-1">Анализ требований</h6>
<p class="small text-muted mb-0">Детальное изучение ваших потребностей</p>
</div>
</div>
</div>
<div class="step-card bg-white rounded-4 p-4 shadow mb-4">
<div class="d-flex align-items-center">
<div class="step-number bg-secondary text-white rounded-circle me-3"
style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
2
</div>
<div>
<h6 class="mb-1">Проектирование</h6>
<p class="small text-muted mb-0">Создание архитектуры и дизайна</p>
</div>
</div>
</div>
<div class="step-card bg-white rounded-4 p-4 shadow mb-4">
<div class="d-flex align-items-center">
<div class="step-number bg-success text-white rounded-circle me-3"
style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
3
</div>
<div>
<h6 class="mb-1">Разработка</h6>
<p class="small text-muted mb-0">Программирование и тестирование</p>
</div>
</div>
</div>
<div class="step-card bg-white rounded-4 p-4 shadow">
<div class="d-flex align-items-center">
<div class="step-number bg-warning text-white rounded-circle me-3"
style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
4
</div>
<div>
<h6 class="mb-1">Запуск и поддержка</h6>
<p class="small text-muted mb-0">Деплой и техническая поддержка</p>
</div>
</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="{% url 'services' %}" class="btn btn-light btn-lg text-primary">
<i class="fas fa-comments me-2"></i>
Получить консультацию
</a>
<a href="tel:+82-10-XXXX-XXXX" class="btn btn-outline-light btn-lg">
<i class="fas fa-phone me-2"></i>
Позвонить сейчас
</a>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Animate elements on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-fade-in-up');
}
});
}, observerOptions);
// Observe service cards
document.querySelectorAll('.service-card, .step-card').forEach(card => {
observer.observe(card);
});
});
</script>
<style>
.max-width-600 {
max-width: 600px;
}
.hero-graphic {
perspective: 1000px;
}
.code-window {
transform-style: preserve-3d;
}
.floating-icon {
animation-delay: 1s;
}
.step-card {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s ease;
}
.step-card.animate-fade-in-up {
opacity: 1;
transform: translateY(0);
}
.feature-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
@media (max-width: 768px) {
.hero-graphic {
margin-top: 2rem;
}
.code-window {
transform: rotate(0deg) !important;
max-width: 100% !important;
}
.mobile-preview {
position: relative !important;
transform: rotate(0deg) !important;
margin-top: 1rem;
width: 100% !important;
}
.floating-icon {
position: relative !important;
display: inline-block;
margin: 0.5rem;
}
}
</style>
{% endblock %}

View File

@@ -0,0 +1,99 @@
{% load static %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<title>Модальное окно для заявки на услугу</title>
<style>
/* Стили для модального окна */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
#qrCodeImg {
display: none;
margin: 20px auto;
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<!-- Модальное окно -->
<div id="serviceModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<form id="serviceRequestForm">
<div class="form-group">
<label for="clientName">Ваше имя:</label>
<input type="text" class="form-control" id="clientName" name="client_name" required>
</div>
<div class="form-group">
<label for="clientEmail">Ваш email:</label>
<input type="email" class="form-control" id="clientEmail" name="client_email" required>
</div>
<div class="form-group">
<label for="clientPhone">Ваш телефон:</label>
<input type="text" class="form-control" id="clientPhone" name="client_phone" required pattern="^\+?[0-9\s\-]{7,15}$">
</div>
<div class="form-group">
<label for="description">Описание заявки:</label>
<textarea class="form-control" id="description" name="description" required></textarea>
</div>
<div id="qrCodeContainer">
<p>QR-код для завершения регистрации:</p>
<img id="qrCodeImg" src="" alt="QR Code">
</div>
<button type="button" id="generateQrButton" class="btn btn-primary">Продолжить</button>
</form>
</div>
</div>
<div id="confirmationModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h4>Заявка успешно создана!</h4>
<p>Ваши данные были отправлены и заявка зарегистрирована. Пожалуйста, проверьте ваш Telegram для получения подтверждения.</p>
</div>
</div>
<script <script src="{% static 'assets/js/modal-init.js' %}"> </script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,14 @@
{% load static %}
<nav class="navbar navbar-expand-md bg-dark py-3" data-bs-theme="dark">
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon"><svg class="bi bi-bezier" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M0 10.5A1.5 1.5 0 0 1 1.5 9h1A1.5 1.5 0 0 1 4 10.5v1A1.5 1.5 0 0 1 2.5 13h-1A1.5 1.5 0 0 1 0 11.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm10.5.5A1.5 1.5 0 0 1 13.5 9h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zM6 4.5A1.5 1.5 0 0 1 7.5 3h1A1.5 1.5 0 0 1 10 4.5v1A1.5 1.5 0 0 1 8.5 7h-1A1.5 1.5 0 0 1 6 5.5zM7.5 4a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z"></path>
<path d="M6 4.5H1.866a1 1 0 1 0 0 1h2.668A6.517 6.517 0 0 0 1.814 9H2.5c.123 0 .244.015.358.043a5.517 5.517 0 0 1 3.185-3.185A1.503 1.503 0 0 1 6 5.5zm3.957 1.358A1.5 1.5 0 0 0 10 5.5v-1h4.134a1 1 0 1 1 0 1h-2.668a6.517 6.517 0 0 1 2.72 3.5H13.5c-.123 0-.243.015-.358.043a5.517 5.517 0 0 0-3.185-3.185z"></path>
</svg></span><span>SmartSolTech</span></a><button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navcol-5"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
<div id="navcol-5" class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link active" href="{% url 'services' %}">Услуги</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'about_view' %}">О нас</a></li>
</ul>
</div>
</div>
</nav>

View File

@@ -0,0 +1,127 @@
{% load static %}
<nav class="navbar navbar-expand-lg navbar-modern">
<div class="container-modern">
<a class="navbar-brand-modern" href="{% url 'home' %}">
<i class="fas fa-code me-2"></i>
SmartSolTech
</a>
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Переключить навигацию">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'home' %}active{% endif %}"
href="{% url 'home' %}">
<i class="fas fa-home me-2"></i>Главная
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'services' %}active{% endif %}"
href="{% url 'services' %}">
<i class="fas fa-cog me-2"></i>Услуги
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'about' %}active{% endif %}"
href="{% url 'about' %}">
<i class="fas fa-info-circle me-2"></i>О нас
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern" href="#contact">
<i class="fas fa-envelope me-2"></i>Контакты
</a>
</li>
<li class="nav-item ms-3">
<a class="btn btn-primary-modern" href="{% url 'services' %}">
<i class="fas fa-rocket me-2"></i>Начать проект
</a>
</li>
</ul>
</div>
</div>
</nav>
<style>
.navbar-toggler {
position: relative;
width: 40px;
height: 40px;
border: none !important;
outline: none !important;
box-shadow: none !important;
}
.navbar-toggler:focus {
box-shadow: none !important;
}
.navbar-toggler-icon {
display: block;
width: 25px;
height: 2px;
background-color: var(--text-dark);
position: relative;
transition: all 0.3s ease;
margin: auto;
}
.navbar-toggler-icon::before,
.navbar-toggler-icon::after {
content: '';
position: absolute;
width: 25px;
height: 2px;
background-color: var(--text-dark);
transition: all 0.3s ease;
}
.navbar-toggler-icon::before {
top: -8px;
}
.navbar-toggler-icon::after {
top: 8px;
}
.navbar-toggler[aria-expanded="true"] .navbar-toggler-icon {
background-color: transparent;
}
.navbar-toggler[aria-expanded="true"] .navbar-toggler-icon::before {
transform: rotate(45deg);
top: 0;
}
.navbar-toggler[aria-expanded="true"] .navbar-toggler-icon::after {
transform: rotate(-45deg);
top: 0;
}
@media (max-width: 991.98px) {
.navbar-collapse {
margin-top: 1rem;
padding: 1.5rem;
background: var(--bg-light);
border-radius: 16px;
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
}
.nav-link-modern {
padding: 0.75rem 1rem;
margin: 0.25rem 0;
border-radius: 12px;
}
.btn-primary-modern {
margin-top: 1rem;
width: 100%;
justify-content: center;
}
}
</style>

View File

@@ -0,0 +1,81 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container py-4 py-xl-5">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Отзывы</h2>
</div>
</div>
<div class="row gy-4 row-cols-1 row-cols-sm-2 row-cols-lg-3">
{% for review in reviews %}
<div class="col">
<div class="card">
<div class="card-body">
<p class="bg-body-tertiary border rounded border-0 p-4">{{ review.comment }}</p>
<div class="d-flex">
{% if review.client.image %}
<img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="{{ review.client.image.url }}" />
{% else %}
<img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="https://cdn.bootstrapstudio.io/placeholders/1400x800.png" />
{% endif %}
<div>
<p class="fw-bold text-primary mb-0">{{ review.client.first_name }} {{ review.client.last_name }}</p>
<p class="text-muted mb-0">Оценка: {{ review.rating }} из 5</p>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div id="orderModal" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Оформление заявки на услугу</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="post">
<div class="form-group mb-3">
<label class="form-label">Выбор услуги</label>
<select id="service" class="form-select" name="service">
{% for service in services %}
<option value="{{ service.id }}">{{ service.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group mb-3">
<label class="form-label">Имя</label>
<input id="name" class="form-control" type="text" name="name" required />
</div>
<div class="form-group mb-3">
<label class="form-label">Телефон</label>
<input id="phone" class="form-control" type="tel" name="phone" required />
</div>
<div class="form-group mb-3">
<label class="form-label">Адрес электронной почты</label>
<input id="email" class="form-control" type="email" name="email" required />
</div>
<div class="form-group mb-3">
<label class="form-label">Описание услуги</label>
<textarea id="description" class="form-control" name="description" rows="4"></textarea>
</div>
<div class="form-group mb-3">
<label class="form-label">Дата связи</label>
<input id="contact_date" class="form-control" type="date" name="contact_date" required />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Отправить заявку</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,14 @@
{% extends 'web/base.html' %}
{% block content %}
<h2>Детали заказа</h2>
<p><strong>Номер заказа:</strong> {{ order.id }}</p>
<p><strong>Услуга:</strong> {{ order.service.name }}</p>
<p><strong>Клиент:</strong> {{ order.client.first_name }} {{ order.client.last_name }}</p>
<p><strong>Сообщение:</strong> {{ order.message }}</p>
<p><strong>Статус:</strong> {{ order.get_status_display }}</p>
<p><strong>Дата создания:</strong> {{ order.order_date }}</p>
<a href="{% url 'home' %}">Вернуться на главную</a>
{% endblock %}

View File

@@ -0,0 +1,94 @@
{% extends 'web/base.html' %}
{% load static %}
{% block title %}Услуга - {{ service.name }}{% endblock %}
{% block content %}
<div class="container py-4 py-xl-5">
<div class="row gy-4 gy-md-0">
<div class="col-md-6">
<div class="p-xl-5 m-xl-5"><img class="rounded img-fluid w-100 fit-cover" style="min-height: 300px;" src="{{ service.image.url }}" /></div>
</div>
<div class="col-md-6 d-md-flex align-items-md-center">
<div style="max-width: 350px;">
<h2 class="text-uppercase fw-bold">{{ service.name }}<br /></h2>
<p class="my-3">{{ service.description }}</p>
<!-- Кнопка открытия модального окна -->
<button id="openModalBtn" class="btn btn-primary" data-service-id="{{ service.id }}">Открыть заявку на услугу</button>
</div>
</div>
</div>
</div>
<!-- Модальное окно для создания заявки на услугу -->
{% include "web/modal_order_form.html" %}
<!-- Проверяем, есть ли проекты, связанные с данной услугой, и показываем раздел, если есть -->
{% if service.projects.exists %}
<div class="container py-4">
<div class="row-cols-auto">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Проекты</h2>
<p>Список проектов, связанных с данной услугой:</p>
</div>
</div>
<div class="row gy-4 row-cols-1 row-cols-sm-2 row-cols-lg-3">
{% for project in service.projects.all %}
<div class="col">
<div class="card project-card">
<div class="card-body">
<h5 class="card-title">{{ project.name }}</h5>
<p class="card-text small-text">{{ project.description }}</p>
<p class="card-text small-text"><strong>Сообщение заказчика:</strong> {{ project.order.message }}</p>
<p class="card-text small-text"><strong>Дата завершения:</strong> {{ project.completion_date }}</p>
<p class="card-text small-text"><strong>Статус:</strong> {{ project.get_status_display }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Проверяем, есть ли отзывы в базе данных, и показываем раздел, если есть -->
{% if reviews %}
<div class="container py-4">
<div class="row-cols-auto">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Отзывы</h2>
<p>Наших любимых клиентов. Спасибо, что Вы с нами!</p>
</div>
</div>
<div class="row gy-4 row-cols-1 row-cols-sm-2 row-cols-lg-3">
{% for review in reviews %}
<div class="col">
<div class="card review-card">
<div class="card-body">
<p class="card-text small-text">{{ review.comment }}</p>
<div class="d-flex">
{% if review.client.image %}
<img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="{{ review.client.image.url }}" />
{% else %}
<img class="rounded-circle flex-shrink-0 me-3 fit-cover" width="50" height="50" src="https://cdn.bootstrapstudio.io/placeholders/1400x800.png" />
{% endif %}
<div>
<p class="fw-bold text-primary mb-0 small-text">{{ review.client.first_name }} {{ review.client.last_name }}</p>
<p class="text-muted mb-0 small-text">Оценка: {{ review.rating }} из 5</p>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Подключение JavaScript файлов, вынесенных в директорию static/assets/js -->
<script src="{% static 'assets/js/get-csrf-token.js' %}"></script>
<script src="{% static 'assets/js/modal-init.js' %}"></script>
<script src="{% static 'assets/js/service_request.js' %}"></script>
<script src="{% static 'assets/js/verification_status.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'web/base.html' %}
{% block content %}
<div class="container mt-4">
<h2>Заявка успешно создана</h2>
<p>Спасибо, ваша заявка на услугу "{{ service_request.service.name }}" успешно создана.</p>
<p>Вы также можете перейти к своему заказу ниже:</p>
<a href="{% url 'order_detail' order.pk %}" class="btn btn-primary">Перейти к заказу</a>
</div>
{% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends 'web/base.html' %}
{% load static %}
{% block content %}
<div class="container py-4 py-xl-5">
<div class="row mb-5">
<div class="col-md-8 col-xl-6 text-center mx-auto">
<h2>Наши Услуги</h2>
<p class="w-lg-50">Предоставляем современные решения для вашего бизнеса. Ознакомьтесь с нашими услугами ниже.</p>
</div>
</div>
<div class="row gy-4 row-cols-1 row-cols-md-2 row-cols-xl-3">
{% for service in services %}
<div class="col">
<div class="card">
{% if service.image %}
<img class="card-img-top w-100 d-block fit-cover" style="width: 300px; height: 200px; object-fit: cover;" src="{{ service.image.url }}" />
{% else %}
<img class="card-img-top w-100 d-block fit-cover" style="width: 300px; height: 200px; object-fit: cover;" src="{{ service.image}}" />
{% endif %}
<div class="card-body p-4">
<h4 class="card-title">
<a href="{% url 'service_detail' service.pk %}">{{ service.name }}</a>
</h4>
<p class="card-text">{{ service.description }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,657 @@
{% 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">
<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>
</h1>
<p class="lead text-muted mb-5">
От концепции до запуска - мы предоставляем комплексные IT-решения для роста вашего бизнеса
</p>
</div>
</div>
</div>
</section>
<!-- Services Grid -->
<section class="section-padding">
<div class="container-modern">
<!-- Service Categories Filter -->
<div class="text-center mb-5">
<div class="btn-group" role="group" aria-label="Категории услуг">
<button type="button" class="btn btn-outline-primary active" data-filter="all">
Все услуги
</button>
<button type="button" class="btn btn-outline-primary" data-filter="web">
Веб-разработка
</button>
<button type="button" class="btn btn-outline-primary" data-filter="mobile">
Мобильные приложения
</button>
<button type="button" class="btn btn-outline-primary" data-filter="design">
Дизайн
</button>
<button type="button" class="btn btn-outline-primary" data-filter="other">
Другое
</button>
</div>
</div>
<div class="row g-4" id="services-container">
{% for service in services %}
<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 %}
<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">
<i class="fas fa-cogs text-white" style="font-size: 3rem;"></i>
</div>
{% endif %}
<div class="position-absolute top-0 start-0 p-3">
<span class="badge bg-primary">{{ service.category.name }}</span>
</div>
</div>
<div class="card-body d-flex flex-column">
<h5 class="mb-3">{{ service.name }}</h5>
<p class="text-muted flex-grow-1">{{ service.description|truncatewords:15 }}</p>
<!-- Service Features -->
<div class="mb-3">
<small class="text-muted d-block mb-2">Что входит:</small>
<div class="d-flex flex-wrap gap-1">
<span class="badge bg-light text-dark">Консультация</span>
<span class="badge bg-light text-dark">Разработка</span>
<span class="badge bg-light text-dark">Тестирование</span>
<span class="badge bg-light text-dark">Поддержка</span>
</div>
</div>
<!-- Price Range -->
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<small class="text-muted">От</small>
<span class="h5 text-primary mb-0">₩ {{ service.price|default:"По запросу" }}</span>
</div>
<div class="text-end">
<small class="text-muted">Срок</small>
<div class="fw-semibold">2-4 недели</div>
</div>
</div>
<div class="d-flex gap-2">
<a href="{% url 'service_detail' service.pk %}" class="btn btn-outline-primary flex-grow-1">
Подробнее
</a>
<button class="btn btn-primary-modern"
onclick="openServiceModal({{ service.pk }}, '{{ service.name }}')">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- Why Choose Our Services -->
<section class="section-padding bg-light">
<div class="container-modern">
<div class="row justify-content-center text-center mb-5">
<div class="col-lg-8">
<h2 class="display-6 fw-bold mb-4">
Почему выбирают <span class="text-gradient">наши услуги</span>
</h2>
<p class="lead text-muted">
Мы обеспечиваем высокое качество и профессиональный подход в каждом проекте
</p>
</div>
</div>
<div class="row g-4">
<div class="col-lg-3 col-md-6">
<div class="text-center">
<div class="service-icon mx-auto mb-3 bg-primary">
<i class="fas fa-clock text-white"></i>
</div>
<h5>Быстро</h5>
<p class="text-muted">Соблюдаем сроки и работаем оперативно</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="text-center">
<div class="service-icon mx-auto mb-3 bg-success">
<i class="fas fa-shield-alt text-white"></i>
</div>
<h5>Надежно</h5>
<p class="text-muted">Гарантия качества и стабильность работы</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="text-center">
<div class="service-icon mx-auto mb-3 bg-warning">
<i class="fas fa-headset text-white"></i>
</div>
<h5>Поддержка</h5>
<p class="text-muted">24/7 техническая поддержка проектов</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="text-center">
<div class="service-icon mx-auto mb-3 bg-info">
<i class="fas fa-chart-line text-white"></i>
</div>
<h5>Результативно</h5>
<p class="text-muted">Фокус на бизнес-результате клиента</p>
</div>
</div>
</div>
</div>
</section>
<!-- Process Section -->
<section class="section-padding">
<div class="container-modern">
<div class="row justify-content-center text-center mb-5">
<div class="col-lg-8">
<h2 class="display-6 fw-bold mb-4">
Как мы <span class="text-gradient">работаем</span>
</h2>
<p class="lead text-muted">
Простой и прозрачный процесс от идеи до готового решения
</p>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="position-relative">
<!-- Timeline -->
<div class="timeline">
<div class="timeline-item">
<div class="timeline-marker bg-primary">1</div>
<div class="timeline-content">
<h5>Анализ и планирование</h5>
<p class="text-muted">Изучаем ваши потребности и составляем техническое задание</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-marker bg-secondary">2</div>
<div class="timeline-content">
<h5>Дизайн и прототипирование</h5>
<p class="text-muted">Создаем дизайн-макеты и интерактивные прототипы</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-marker bg-success">3</div>
<div class="timeline-content">
<h5>Разработка и тестирование</h5>
<p class="text-muted">Программируем решение и тщательно тестируем</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-marker bg-warning">4</div>
<div class="timeline-content">
<h5>Запуск и сопровождение</h5>
<p class="text-muted">Запускаем проект и обеспечиваем техническую поддержку</p>
</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">
<button class="btn btn-light btn-lg text-primary" onclick="openServiceModal(0, 'Консультация')">
<i class="fas fa-comments me-2"></i>
Бесплатная консультация
</button>
<a href="#" class="btn btn-outline-light btn-lg">
<i class="fas fa-download me-2"></i>
Скачать прайс-лист
</a>
</div>
</div>
</div>
</div>
</section>
<!-- Service Request Modal -->
<div class="modal fade" id="serviceModal" tabindex="-1" aria-labelledby="serviceModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow-lg">
<div class="modal-header bg-gradient text-white border-0">
<h5 class="modal-title" id="serviceModalLabel">
<i class="fas fa-paper-plane me-2"></i>
Заказать услугу
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<form id="serviceRequestForm">
{% csrf_token %}
<input type="hidden" id="serviceId" name="service_id">
<div class="row g-3">
<div class="col-md-6">
<label for="firstName" class="form-label">Имя *</label>
<input type="text" class="form-control" id="firstName" name="first_name" required>
</div>
<div class="col-md-6">
<label for="lastName" class="form-label">Фамилия *</label>
<input type="text" class="form-control" id="lastName" name="last_name" required>
</div>
<div class="col-md-6">
<label for="email" class="form-label">Email *</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="col-md-6">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" class="form-control" id="phone" name="phone">
</div>
<div class="col-12">
<label for="description" class="form-label">Описание проекта *</label>
<textarea class="form-control" id="description" name="description" rows="4"
placeholder="Опишите ваш проект, цели и требования..." required></textarea>
</div>
<div class="col-md-6">
<label for="budget" class="form-label">Примерный бюджет</label>
<select class="form-select" id="budget" name="budget">
<option value="">Не определен</option>
<option value="1000-5000">₩ 1,000,000 - 5,000,000</option>
<option value="5000-10000">₩ 5,000,000 - 10,000,000</option>
<option value="10000+">₩ 10,000,000+</option>
</select>
</div>
<div class="col-md-6">
<label for="timeline" class="form-label">Желаемые сроки</label>
<select class="form-select" id="timeline" name="timeline">
<option value="">Не определены</option>
<option value="urgent">Срочно (1-2 недели)</option>
<option value="normal">Обычно (1-2 месяца)</option>
<option value="flexible">Гибкие сроки</option>
</select>
</div>
</div>
<!-- QR Code Section (Hidden by default) -->
<div class="mt-4" id="qrCodeSection" style="display: none;">
<div class="alert alert-info text-center">
<h6><i class="fas fa-qrcode me-2"></i>Завершите регистрацию через Telegram</h6>
<p class="mb-3">Отсканируйте QR-код или перейдите по ссылке для подтверждения заявки:</p>
<div class="d-flex justify-content-center mb-3">
<img id="qrCodeImage" src="" alt="QR Code" class="img-fluid border rounded" style="max-width: 200px; min-width: 200px; height: 200px; object-fit: contain; display: none;">
</div>
<div class="mb-3">
<a id="telegramLink" href="" target="_blank" class="btn btn-info">
<i class="fab fa-telegram-plane me-2"></i>Открыть в Telegram
</a>
</div>
<div class="d-flex align-items-center justify-content-center">
<div class="spinner-border spinner-border-sm text-primary me-2" role="status" aria-hidden="true"></div>
<small class="text-muted">Ожидаем подтверждения в Telegram...</small>
</div>
</div>
</div>
<!-- Success Animation Section (Hidden by default) -->
<div class="mt-4" id="successSection" style="display: none;">
<div class="text-center py-5">
<div class="success-checkmark">
<div class="check-icon">
<span class="icon-line line-tip"></span>
<span class="icon-line line-long"></span>
<div class="icon-circle"></div>
<div class="icon-fix"></div>
</div>
</div>
<h4 class="text-success mt-3 mb-2">Заявка подана успешно!</h4>
<p class="text-muted">Мы свяжемся с вами в ближайшее время</p>
</div>
</div>
<div class="mt-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="agreeTerms" required>
<label class="form-check-label small" for="agreeTerms">
Я соглашаюсь с <a href="#" class="text-primary">условиями обработки персональных данных</a>
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="submit" form="serviceRequestForm" class="btn btn-primary-modern">
<i class="fas fa-paper-plane me-2"></i>
Отправить заявку
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
// Service filtering
document.addEventListener('DOMContentLoaded', function() {
const filterButtons = document.querySelectorAll('[data-filter]');
const serviceItems = document.querySelectorAll('.service-item');
filterButtons.forEach(button => {
button.addEventListener('click', function() {
const filter = this.getAttribute('data-filter');
// Update active button
filterButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
// Filter services
serviceItems.forEach(item => {
if (filter === 'all' || item.getAttribute('data-category').includes(filter)) {
item.style.display = 'block';
setTimeout(() => {
item.style.opacity = '1';
item.style.transform = 'translateY(0)';
}, 50);
} else {
item.style.opacity = '0';
item.style.transform = 'translateY(20px)';
setTimeout(() => {
item.style.display = 'none';
}, 300);
}
});
});
});
});
// Service modal
function openServiceModal(serviceId, serviceName) {
document.getElementById('serviceId').value = serviceId;
document.getElementById('serviceModalLabel').innerHTML =
'<i class="fas fa-paper-plane me-2"></i>Заказать услугу: ' + serviceName;
const modal = new bootstrap.Modal(document.getElementById('serviceModal'));
modal.show();
}
// Form submission
document.getElementById('serviceRequestForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = document.querySelector('button[type="submit"][form="serviceRequestForm"]');
const originalContent = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Отправляем...';
submitBtn.disabled = true;
// Получаем CSRF токен
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
// Подготавливаем данные для отправки
const serviceId = document.getElementById('serviceId').value;
const requestData = {
client_name: formData.get('first_name') + ' ' + formData.get('last_name'),
client_email: formData.get('email'),
client_phone: formData.get('phone') || '',
description: formData.get('description'),
budget: formData.get('budget') || '',
timeline: formData.get('timeline') || ''
};
// Отправляем запрос на создание QR-кода
fetch(`/service/generate_qr_code/${serviceId}/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
if (data.qr_code_url) {
// Показываем секцию с QR-кодом
const qrSection = document.getElementById('qrCodeSection');
const qrImage = document.getElementById('qrCodeImage');
const telegramLink = document.getElementById('telegramLink');
qrImage.src = data.qr_code_url;
qrImage.style.display = 'block';
telegramLink.href = data.registration_link;
qrSection.style.display = 'block';
// Обновляем кнопку
submitBtn.innerHTML = '<i class="fas fa-telegram-plane me-2"></i>Перейдите в Telegram для подтверждения';
submitBtn.disabled = true;
// Начинаем проверку статуса подтверждения
const serviceRequestId = data.service_request_id;
confirmationCheckInterval = setInterval(() => {
checkConfirmationStatus(serviceRequestId, confirmationCheckInterval);
}, 3000); // Проверяем каждые 3 секунды
} else {
throw new Error(data.error || 'Ошибка при создании заявки');
}
})
.catch(error => {
console.error('Error:', error);
submitBtn.innerHTML = originalContent;
submitBtn.disabled = false;
submitBtn.classList.remove('btn-success');
submitBtn.classList.add('btn-primary-modern');
showNotification('Произошла ошибка при создании заявки. Попробуйте еще раз.', 'error');
});
});
// Функция проверки статуса подтверждения
function checkConfirmationStatus(serviceRequestId, checkInterval) {
fetch(`/service/check_status/${serviceRequestId}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.is_verified) {
// Останавливаем проверку
clearInterval(checkInterval);
// Скрываем QR-код
const qrSection = document.getElementById('qrCodeSection');
qrSection.style.display = 'none';
// Показываем анимацию успеха
const successSection = document.getElementById('successSection');
successSection.style.display = 'block';
// Запускаем анимацию галочки
setTimeout(() => {
const checkmark = successSection.querySelector('.success-checkmark');
checkmark.classList.add('animate');
}, 100);
// Закрываем модальное окно через 3 секунды
setTimeout(() => {
const modal = bootstrap.Modal.getInstance(document.getElementById('serviceModal'));
modal.hide();
showNotification('Заявка подтверждена! Спасибо за регистрацию в Telegram.', 'success');
}, 3000);
}
})
.catch(error => {
console.log('Ожидаем подтверждения...', error);
});
}
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `alert alert-${type} position-fixed top-0 end-0 m-3`;
notification.style.zIndex = '9999';
notification.innerHTML = `
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
${message}
<button type="button" class="btn-close" onclick="this.parentElement.remove()"></button>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 5000);
}
// Reset modal when it's hidden
let confirmationCheckInterval = null;
document.getElementById('serviceModal').addEventListener('hidden.bs.modal', function() {
// Останавливаем проверку подтверждения
if (confirmationCheckInterval) {
clearInterval(confirmationCheckInterval);
confirmationCheckInterval = null;
}
// Сброс формы
document.getElementById('serviceRequestForm').reset();
// Скрытие всех секций
document.getElementById('qrCodeSection').style.display = 'none';
document.getElementById('qrCodeImage').style.display = 'none';
document.getElementById('successSection').style.display = 'none';
// Убираем анимацию галочки
const checkmark = document.querySelector('.success-checkmark');
if (checkmark) {
checkmark.classList.remove('animate');
}
// Сброс кнопки отправки
const submitBtn = document.querySelector('button[type="submit"][form="serviceRequestForm"]');
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Отправить заявку';
submitBtn.disabled = false;
submitBtn.classList.remove('btn-success');
submitBtn.classList.add('btn-primary-modern');
});
</script>
<style>
.timeline {
position: relative;
}
.timeline::before {
content: '';
position: absolute;
left: 30px;
top: 0;
bottom: 0;
width: 2px;
background: var(--border-color);
}
.timeline-item {
position: relative;
padding-left: 80px;
margin-bottom: 2rem;
}
.timeline-marker {
position: absolute;
left: 0;
top: 0;
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1.25rem;
z-index: 1;
}
.timeline-content {
background: var(--bg-light);
padding: 1.5rem;
border-radius: 12px;
border: 1px solid var(--border-color);
}
.service-item {
transition: all 0.3s ease;
}
.btn-group .btn {
border-radius: 25px !important;
margin: 0 5px;
}
.btn-group .btn.active {
background: var(--gradient-primary);
border-color: transparent;
color: white;
}
@media (max-width: 768px) {
.timeline::before {
left: 15px;
}
.timeline-item {
padding-left: 50px;
}
.timeline-marker {
width: 40px;
height: 40px;
font-size: 1rem;
}
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.btn-group .btn {
margin: 0;
}
}
</style>
{% endblock %}

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

29
smartsoltech/web/urls.py Normal file
View File

@@ -0,0 +1,29 @@
# web/urls.py
from django.urls import path
from . import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('', views.home, name='home'),
path('service/<int:pk>/', views.service_detail, name='service_detail'),
path('project/<int:pk>/', views.project_detail, name='project_detail'),
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('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'),
path('service/check_status/<int:request_id>/', views.check_request_status, name='check_request_status'),
path('service/request_status/<int:service_id>/', views.request_status, name='request_status'),
path('service/request/<int:service_id>/', views.create_service_request, name='create_service_request'),
path('complete_registration/<int:request_id>/', views.complete_registration, name='complete_registration'),
# path('complete_registration/', views.complete_registration_basic, name='complete_registration_basic'),
# path('service/check_service_request_data/', views.check_service_request_data, name='check_service_request_data'),
# path('client/orders/', views.client_orders, name='client_orders'),
# path('order/<int:pk>/', views.order_detail, name='order_detail'),
path('service/send_telegram_notification/', views.send_telegram_notification, name='send_telegram_notification'),
# path('service/create_request/', views.create_service_request_basic, name='create_service_request_basic'),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

317
smartsoltech/web/views.py Normal file
View File

@@ -0,0 +1,317 @@
from django.shortcuts import render, get_object_or_404, redirect
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest
from django.db.models import Avg
from comunication.models import TelegramSettings
import qrcode
import os
from django.conf import settings
import uuid
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
from django.http import JsonResponse
from django.utils.crypto import get_random_string
from django.contrib.auth.models import User
from decouple import config
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from comunication.telegram_bot import TelegramBot
import hmac
import hashlib
import json
from django.views.decorators.csrf import csrf_exempt
import logging
from django.db import transaction, IntegrityError
from django.db.models import Q
logger = logging.getLogger(__name__)
# Initialize Telegram Bot
try:
bot = TelegramBot()
except Exception as e:
logger.error(f"Failed to initialize Telegram bot: {str(e)}")
def home(request):
services = Service.objects.all()[:6] # Показываем только первые 6 услуг на главной
return render(request, 'web/home_modern.html', {'services': services})
def service_detail(request, pk):
service = get_object_or_404(Service, pk=pk)
projects_in_category = Project.objects.filter(category=service.category)
average_rating = service.reviews.aggregate(Avg('rating'))['rating__avg'] or 0
total_reviews = service.reviews.count()
reviews = service.reviews.all()
return render(request, 'web/service_detail.html', {
'service': service,
'projects_in_category': projects_in_category,
'average_rating': average_rating,
'total_reviews': total_reviews,
'reviews': reviews,
})
def project_detail(request, pk):
project = get_object_or_404(Project, pk=pk)
return render(request, 'web/project_detail.html', {'project': project})
def client_detail(request, pk):
client = get_object_or_404(Client, pk=pk)
return render(request, 'web/client_detail.html', {'client': client})
def blog_post_detail(request, pk):
blog_post = get_object_or_404(BlogPost, pk=pk)
return render(request, 'web/blog_post_detail.html', {'blog_post': blog_post})
def services_view(request):
services = Service.objects.all()
return render(request, 'web/services_modern.html', {'services': services})
def about_view(request):
return render(request, 'web/about_modern.html')
def create_service_request(request, service_id):
if request.method == 'POST':
try:
data = json.loads(request.body)
client_email = data.get('client_email')
client_phone = data.get('client_phone')
client_name = data.get('client_name')
if not all([client_email, client_phone, client_name]):
return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены'}, status=400)
service = get_object_or_404(Service, pk=service_id)
# Проверяем наличие клиента по email (так как email должен быть уникальным)
client, client_created = Client.objects.get_or_create(
email=client_email,
defaults={
'first_name': client_name.split()[0] if client_name else "",
'last_name': client_name.split()[-1] if len(client_name.split()) > 1 else "",
'phone_number': client_phone,
}
)
# Обновляем данные клиента, если он уже существовал (например, телефон или имя изменились)
if not client_created:
client.first_name = client_name.split()[0]
client.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
client.phone_number = client_phone
client.save()
# Проверяем наличие заявки на эту же услугу, не завершенной и не подтвержденной
existing_requests = ServiceRequest.objects.filter(client=client, service=service, is_verified=False)
if existing_requests.exists():
return JsonResponse({
'status': 'existing_request',
'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.'
})
# Создаем новую заявку для клиента
token = uuid.uuid4().hex
service_request = ServiceRequest.objects.create(
service=service,
client=client,
token=token,
is_verified=False
)
return JsonResponse({
'status': 'success',
'message': 'Заявка успешно создана. Пожалуйста, проверьте ваш Telegram для подтверждения.',
'service_request_id': service_request.id,
})
except json.JSONDecodeError:
logger.error("Invalid JSON format")
return JsonResponse({'status': 'error', 'message': 'Неверный формат данных'}, status=400)
return JsonResponse({'status': 'error', 'message': 'Метод запроса должен быть POST'}, status=405)
def generate_qr_code(request, service_id):
if request.method == 'POST':
try:
data = json.loads(request.body)
client_email = data.get('client_email')
client_phone = data.get('client_phone')
client_name = data.get('client_name')
if not all([client_email, client_phone, client_name]):
logger.error("Все поля должны быть заполнены")
return JsonResponse({'error': 'Все поля должны быть заполнены'}, status=400)
# Используем транзакцию для предотвращения конкурентного создания дубликатов
with transaction.atomic():
user, user_created = User.objects.select_for_update().get_or_create(
email=client_email,
defaults={
"username": f"{client_email.split('@')[0]}_{get_random_string(5)}",
"first_name": client_name.split()[0] if client_name else "",
"last_name": client_name.split()[-1] if len(client_name.split()) > 1 else ""
}
)
if not user_created:
# Обновляем информацию о пользователе, если он уже существует
user.first_name = client_name.split()[0] if client_name else ""
user.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
user.save()
client, client_created = Client.objects.select_for_update().get_or_create(
email=client_email,
defaults={
'user': user,
'first_name': user.first_name,
'last_name': user.last_name,
'phone_number': client_phone
}
)
if not client_created:
# Обновляем информацию о клиенте, если он уже существует
client.first_name = user.first_name
client.last_name = user.last_name
client.phone_number = client_phone
client.save()
# Проверка на существование активной заявки
service = get_object_or_404(Service, pk=service_id)
existing_requests = ServiceRequest.objects.filter(client=client, service=service, is_verified=False)
if existing_requests.exists():
return JsonResponse({
'status': 'existing_request',
'message': 'У вас уже есть активная заявка на данную услугу. Пожалуйста, проверьте ваш Telegram для завершения процесса.'
})
# Создаем новую заявку на услугу
token = uuid.uuid4().hex
service_request = ServiceRequest.objects.create(
service=service,
client=client,
token=token,
is_verified=False
)
logger.info(f"Создана новая заявка: {service_request.id} для клиента: {client.email}")
# Генерация ссылки и QR-кода для Telegram
telegram_settings = get_object_or_404(TelegramSettings, pk=1)
registration_link = f'https://t.me/{telegram_settings.bot_name}?start=request_{service_request.id}_token_{urlsafe_base64_encode(force_bytes(token))}'
qr = qrcode.make(registration_link)
qr_code_dir = os.path.join(settings.STATICFILES_DIRS[0], 'qr_codes')
qr_code_path = os.path.join(qr_code_dir, f"request_{service_request.id}.png")
external_qr_link = f'static/qr_codes/request_{service_request.id}.png'
if not os.path.exists(qr_code_dir):
os.makedirs(qr_code_dir)
qr.save(qr_code_path)
except IntegrityError as e:
logger.error(f"Ошибка целостности данных при создании пользователя или клиента: {str(e)}")
return JsonResponse({'error': 'Ошибка при обработке данных. Пожалуйста, попробуйте позже.'}, status=500)
except Exception as e:
logger.error(f"Ошибка при обработке запроса: {str(e)}")
return JsonResponse({'error': f'Ошибка: {str(e)}'}, status=500)
return JsonResponse({
'registration_link': registration_link,
'qr_code_url': f"/{external_qr_link}",
'service_request_id': service_request.id,
'client_email': client_email,
'client_phone': client_phone,
'client_name': client_name
})
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)
def request_status(request, service_id):
try:
service_request = get_object_or_404(ServiceRequest, pk=service_id)
client = service_request.client
is_verified = service_request.is_verified
return JsonResponse({
'is_verified': is_verified,
'client_name': client.first_name if client else "Неизвестно",
'client_chat_id': client.chat_id if client else None,
})
except Exception as e:
logger.error(f"Ошибка при получении статуса заявки: {str(e)}")
return JsonResponse({'error': str(e)}, status=500)
@csrf_exempt
def send_telegram_notification(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
# Логируем полученные данные для отладки
logging.info(f"Полученные данные для подтверждения заявки: {data}")
service_request_id = data.get('service_request_id')
client_chat_id = data.get('client_chat_id')
client_name = data.get('client_name')
if not service_request_id or not client_chat_id:
return JsonResponse({'error': 'Недостаточно данных для подтверждения'}, status=400)
# Проверяем существование заявки
service_request = ServiceRequest.objects.filter(id=service_request_id).first()
if not service_request:
return JsonResponse({'error': 'Заявка не найдена'}, status=404)
# Обновляем заявку с chat_id, если все верно
service_request.chat_id = client_chat_id
service_request.is_verified = True
service_request.save()
return JsonResponse({'status': 'Уведомление успешно отправлено в Telegram'})
except json.JSONDecodeError as e:
logging.error(f"Ошибка при декодировании JSON: {e}")
return JsonResponse({'error': 'Неверный формат данных'}, status=400)
logging.error(f"Неподдерживаемый метод запроса: {request.method}")
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)
def generate_secure_token(service_request_id, secret_key):
"""Генерация безопасного токена для подтверждения подлинности запроса."""
data = f'{service_request_id}:{secret_key}'
return hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest()
def complete_registration(request, request_id):
service_request = get_object_or_404(ServiceRequest, pk=request_id)
if request.method == 'POST':
client_email = request.POST.get('client_email', service_request.client.email)
client_phone = request.POST.get('client_phone', service_request.client.phone_number)
chat_id = request.POST.get('chat_id', service_request.chat_id)
if not all([client_email, client_phone, chat_id]):
return JsonResponse({'status': 'error', 'message': 'Все поля должны быть заполнены.'}, status=400)
client = service_request.client
client.email = client_email
client.phone_number = client_phone
client.save()
service_request.chat_id = chat_id
service_request.save()
return JsonResponse({'status': 'success', 'message': 'Регистрация успешно завершена.'})
return render(request, 'web/complete_registration.html', {'service_request': service_request})
def check_request_status(request, request_id):
"""API endpoint для проверки статуса подтверждения заявки"""
try:
service_request = get_object_or_404(ServiceRequest, pk=request_id)
return JsonResponse({
'is_verified': service_request.is_verified,
'chat_id': service_request.chat_id,
'created_at': service_request.created_at.isoformat() if service_request.created_at else None
})
except ServiceRequest.DoesNotExist:
return JsonResponse({'error': 'Заявка не найдена'}, status=404)
except Exception as e:
logger.error(f"Ошибка при проверке статуса заявки {request_id}: {str(e)}")
return JsonResponse({'error': 'Ошибка сервера'}, status=500)