init commit
This commit is contained in:
0
smartsoltech/web/__init__.py
Normal file
0
smartsoltech/web/__init__.py
Normal file
48
smartsoltech/web/admin.py
Normal file
48
smartsoltech/web/admin.py
Normal 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
13
smartsoltech/web/apps.py
Normal 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
10
smartsoltech/web/forms.py
Normal 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)
|
||||
115
smartsoltech/web/migrations/0001_initial.py
Normal file
115
smartsoltech/web/migrations/0001_initial.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
21
smartsoltech/web/migrations/0003_client_user.py
Normal file
21
smartsoltech/web/migrations/0003_client_user.py
Normal 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),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
20
smartsoltech/web/migrations/0006_order_service_request.py
Normal file
20
smartsoltech/web/migrations/0006_order_service_request.py
Normal 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,
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
0
smartsoltech/web/migrations/__init__.py
Normal file
0
smartsoltech/web/migrations/__init__.py
Normal file
155
smartsoltech/web/models.py
Normal file
155
smartsoltech/web/models.py
Normal 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}"
|
||||
|
||||
22
smartsoltech/web/signals.py
Normal file
22
smartsoltech/web/signals.py
Normal 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
0
smartsoltech/web/templates/.gitignore
vendored
Normal file
53
smartsoltech/web/templates/web/about.html
Normal file
53
smartsoltech/web/templates/web/about.html
Normal 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 %}
|
||||
556
smartsoltech/web/templates/web/about_modern.html
Normal file
556
smartsoltech/web/templates/web/about_modern.html
Normal 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 %}
|
||||
26
smartsoltech/web/templates/web/base.html
Normal file
26
smartsoltech/web/templates/web/base.html
Normal 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>
|
||||
72
smartsoltech/web/templates/web/base_modern.html
Normal file
72
smartsoltech/web/templates/web/base_modern.html
Normal 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>
|
||||
13
smartsoltech/web/templates/web/client_orders.html
Normal file
13
smartsoltech/web/templates/web/client_orders.html
Normal 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 %}
|
||||
29
smartsoltech/web/templates/web/complete_registration.html
Normal file
29
smartsoltech/web/templates/web/complete_registration.html
Normal 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%}
|
||||
@@ -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%}
|
||||
48
smartsoltech/web/templates/web/footer.html
Normal file
48
smartsoltech/web/templates/web/footer.html
Normal 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>
|
||||
189
smartsoltech/web/templates/web/footer_modern.html
Normal file
189
smartsoltech/web/templates/web/footer_modern.html
Normal 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>
|
||||
20
smartsoltech/web/templates/web/header.html
Normal file
20
smartsoltech/web/templates/web/header.html
Normal 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>
|
||||
35
smartsoltech/web/templates/web/home.html
Normal file
35
smartsoltech/web/templates/web/home.html
Normal 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 %}
|
||||
370
smartsoltech/web/templates/web/home_modern.html
Normal file
370
smartsoltech/web/templates/web/home_modern.html
Normal 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 %}
|
||||
99
smartsoltech/web/templates/web/modal_order_form.html
Normal file
99
smartsoltech/web/templates/web/modal_order_form.html
Normal 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">×</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">×</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>
|
||||
14
smartsoltech/web/templates/web/navbar.html
Normal file
14
smartsoltech/web/templates/web/navbar.html
Normal 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>
|
||||
127
smartsoltech/web/templates/web/navbar_modern.html
Normal file
127
smartsoltech/web/templates/web/navbar_modern.html
Normal 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>
|
||||
81
smartsoltech/web/templates/web/order.html
Normal file
81
smartsoltech/web/templates/web/order.html
Normal 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 %}
|
||||
14
smartsoltech/web/templates/web/order_detail.html
Normal file
14
smartsoltech/web/templates/web/order_detail.html
Normal 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 %}
|
||||
94
smartsoltech/web/templates/web/service_detail.html
Normal file
94
smartsoltech/web/templates/web/service_detail.html
Normal 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 %}
|
||||
10
smartsoltech/web/templates/web/service_request_success.html
Normal file
10
smartsoltech/web/templates/web/service_request_success.html
Normal 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 %}
|
||||
32
smartsoltech/web/templates/web/services.html
Normal file
32
smartsoltech/web/templates/web/services.html
Normal 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 %}
|
||||
657
smartsoltech/web/templates/web/services_modern.html
Normal file
657
smartsoltech/web/templates/web/services_modern.html
Normal 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 %}
|
||||
3
smartsoltech/web/tests.py
Normal file
3
smartsoltech/web/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
29
smartsoltech/web/urls.py
Normal file
29
smartsoltech/web/urls.py
Normal 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
317
smartsoltech/web/views.py
Normal 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)
|
||||
Reference in New Issue
Block a user