prod
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
from django.contrib import admin
|
||||
from .models import (
|
||||
Service, Project, Client, Order, Review, BlogPost,
|
||||
Category, ServiceRequest, AboutPage, FooterSettings, TeamMember
|
||||
Service, Project, Client, Order, Review, BlogPost,
|
||||
Category, ServiceRequest, AboutPage, FooterSettings, TeamMember,
|
||||
PortfolioItem, PrivacyPolicy, TermsOfUse, NewsArticle, CareerVacancy
|
||||
)
|
||||
from .forms import ProjectForm
|
||||
|
||||
@@ -36,13 +37,53 @@ class ReviewAdmin(admin.ModelAdmin):
|
||||
|
||||
@admin.register(BlogPost)
|
||||
class BlogPostAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'published_date')
|
||||
search_fields = ('title',)
|
||||
list_display = ('title', 'status', 'author', 'published_date')
|
||||
list_filter = ('status', 'published_date')
|
||||
search_fields = ('title', 'excerpt', 'author__username')
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
|
||||
@admin.register(Category)
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
list_display = ('name','description')
|
||||
search_fields = ('name',)
|
||||
|
||||
|
||||
@admin.register(PortfolioItem)
|
||||
class PortfolioItemAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'client_name', 'completion_date', 'featured', 'is_active')
|
||||
list_filter = ('featured', 'is_active', 'completion_date')
|
||||
search_fields = ('title', 'client_name')
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
|
||||
|
||||
@admin.register(NewsArticle)
|
||||
class NewsArticleAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'is_published', 'published_date', 'created_at')
|
||||
list_filter = ('is_published', 'published_date', 'created_at')
|
||||
search_fields = ('title', 'excerpt')
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
|
||||
|
||||
@admin.register(CareerVacancy)
|
||||
class CareerVacancyAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'location', 'employment_type', 'is_active', 'posted_at')
|
||||
list_filter = ('employment_type', 'is_active', 'posted_at')
|
||||
search_fields = ('title', 'location')
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
|
||||
|
||||
@admin.register(PrivacyPolicy)
|
||||
class PrivacyPolicyAdmin(admin.ModelAdmin):
|
||||
list_display = ('version', 'effective_date', 'is_active')
|
||||
list_filter = ('is_active', 'effective_date')
|
||||
search_fields = ('version',)
|
||||
|
||||
|
||||
@admin.register(TermsOfUse)
|
||||
class TermsOfUseAdmin(admin.ModelAdmin):
|
||||
list_display = ('version', 'effective_date', 'is_active')
|
||||
list_filter = ('is_active', 'effective_date')
|
||||
search_fields = ('version',)
|
||||
|
||||
@admin.register(ServiceRequest)
|
||||
class ServiceRequestAdmin(admin.ModelAdmin):
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.1 on 2025-11-24 00:29
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0011_teammember'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='teammember',
|
||||
name='telegram',
|
||||
field=models.CharField(blank=True, help_text='Username без @ (например: trevor1985)', max_length=100, verbose_name='Telegram'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Generated by Django 5.1.1 on 2025-11-24 00:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0012_alter_teammember_telegram'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CareerVacancy',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200)),
|
||||
('slug', models.SlugField(blank=True, max_length=220, unique=True)),
|
||||
('location', models.CharField(blank=True, max_length=200)),
|
||||
('employment_type', models.CharField(choices=[('FT', 'Полная занятость'), ('PT', 'Частичная занятость'), ('CT', 'Контракт'), ('IN', 'Стажировка')], default='FT', max_length=2)),
|
||||
('responsibilities', models.TextField()),
|
||||
('requirements', models.TextField()),
|
||||
('desirable', models.TextField(blank=True)),
|
||||
('salary_min', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('salary_max', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('posted_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Вакансия',
|
||||
'verbose_name_plural': 'Вакансии',
|
||||
'ordering': ['-posted_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='NewsArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200)),
|
||||
('slug', models.SlugField(blank=True, max_length=220, unique=True)),
|
||||
('excerpt', models.CharField(blank=True, max_length=300)),
|
||||
('content', models.TextField()),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='static/img/news/')),
|
||||
('is_published', models.BooleanField(default=False)),
|
||||
('published_date', models.DateTimeField(blank=True, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Новость',
|
||||
'verbose_name_plural': 'Новости',
|
||||
'ordering': ['-published_date', '-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PrivacyPolicy',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('version', models.CharField(default='1.0', max_length=50)),
|
||||
('content', models.TextField()),
|
||||
('effective_date', models.DateField(auto_now_add=True)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Политика конфиденциальности',
|
||||
'verbose_name_plural': 'Политики конфиденциальности',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TermsOfUse',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('version', models.CharField(default='1.0', max_length=50)),
|
||||
('content', models.TextField()),
|
||||
('effective_date', models.DateField(auto_now_add=True)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Условия использования',
|
||||
'verbose_name_plural': 'Условия использования',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='blogpost',
|
||||
options={'ordering': ['-published_date'], 'verbose_name': 'Запись блога', 'verbose_name_plural': 'Записи блога'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='author',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blog_posts', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='excerpt',
|
||||
field=models.CharField(blank=True, max_length=400),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='slug',
|
||||
field=models.SlugField(blank=True, max_length=220, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('draft', 'Черновик'), ('published', 'Опубликовано')], default='draft', max_length=20),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='views',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PortfolioItem',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200)),
|
||||
('slug', models.SlugField(blank=True, max_length=220, unique=True)),
|
||||
('description', models.TextField()),
|
||||
('client_name', models.CharField(blank=True, max_length=200)),
|
||||
('completion_date', models.DateField(blank=True, null=True)),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='static/img/portfolio/')),
|
||||
('featured', models.BooleanField(default=False)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='portfolio_items', to='web.category')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Портфолио',
|
||||
'verbose_name_plural': 'Портфолио',
|
||||
'ordering': ['-completion_date'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -2,6 +2,8 @@ from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, User
|
||||
import uuid
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from django.conf import settings
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
@@ -57,18 +59,45 @@ class Client(models.Model):
|
||||
return f"{self.first_name} {self.last_name} {self.chat_id}"
|
||||
|
||||
class BlogPost(models.Model):
|
||||
DRAFT = 'draft'
|
||||
PUBLISHED = 'published'
|
||||
STATUS_CHOICES = [
|
||||
(DRAFT, 'Черновик'),
|
||||
(PUBLISHED, 'Опубликовано'),
|
||||
]
|
||||
|
||||
title = models.CharField(max_length=200)
|
||||
slug = models.SlugField(max_length=220, unique=True, blank=True)
|
||||
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='blog_posts')
|
||||
excerpt = models.CharField(max_length=400, blank=True)
|
||||
content = models.TextField()
|
||||
published_date = models.DateTimeField(auto_now_add=True)
|
||||
image = models.ImageField(upload_to='static/img/blog/', blank=True, null=True)
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=DRAFT)
|
||||
published_date = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
views = models.PositiveIntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Блог'
|
||||
verbose_name_plural = 'Блоги'
|
||||
verbose_name = 'Запись блога'
|
||||
verbose_name_plural = 'Записи блога'
|
||||
ordering = ['-published_date']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
base = slugify(self.title)[:200]
|
||||
slug = base
|
||||
i = 1
|
||||
while BlogPost.objects.filter(slug=slug).exists():
|
||||
slug = f"{base}-{i}"
|
||||
i += 1
|
||||
self.slug = slug
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('blog_detail', kwargs={'slug': self.slug})
|
||||
|
||||
class ServiceRequest(models.Model):
|
||||
service = models.ForeignKey(Service, on_delete=models.CASCADE)
|
||||
@@ -136,6 +165,37 @@ class Project(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class PortfolioItem(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
slug = models.SlugField(max_length=220, unique=True, blank=True)
|
||||
description = models.TextField()
|
||||
client_name = models.CharField(max_length=200, blank=True)
|
||||
completion_date = models.DateField(blank=True, null=True)
|
||||
image = models.ImageField(upload_to='static/img/portfolio/', blank=True, null=True)
|
||||
featured = models.BooleanField(default=False)
|
||||
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, related_name='portfolio_items')
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Портфолио'
|
||||
verbose_name_plural = 'Портфолио'
|
||||
ordering = ['-completion_date']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
base = slugify(self.title)[:200]
|
||||
slug = base
|
||||
i = 1
|
||||
while PortfolioItem.objects.filter(slug=slug).exists():
|
||||
slug = f"{base}-{i}"
|
||||
i += 1
|
||||
self.slug = slug
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
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')
|
||||
@@ -247,6 +307,119 @@ class AboutPage(models.Model):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class PrivacyPolicy(models.Model):
|
||||
version = models.CharField(max_length=50, default='1.0')
|
||||
content = models.TextField()
|
||||
effective_date = models.DateField(auto_now_add=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Политика конфиденциальности'
|
||||
verbose_name_plural = 'Политики конфиденциальности'
|
||||
|
||||
def __str__(self):
|
||||
return f"Privacy Policy v{self.version} ({self.effective_date})"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.is_active:
|
||||
PrivacyPolicy.objects.exclude(pk=self.pk).update(is_active=False)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class TermsOfUse(models.Model):
|
||||
version = models.CharField(max_length=50, default='1.0')
|
||||
content = models.TextField()
|
||||
effective_date = models.DateField(auto_now_add=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Условия использования'
|
||||
verbose_name_plural = 'Условия использования'
|
||||
|
||||
def __str__(self):
|
||||
return f"Terms v{self.version} ({self.effective_date})"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.is_active:
|
||||
TermsOfUse.objects.exclude(pk=self.pk).update(is_active=False)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class NewsArticle(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
slug = models.SlugField(max_length=220, unique=True, blank=True)
|
||||
excerpt = models.CharField(max_length=300, blank=True)
|
||||
content = models.TextField()
|
||||
image = models.ImageField(upload_to='static/img/news/', blank=True, null=True)
|
||||
is_published = models.BooleanField(default=False)
|
||||
published_date = models.DateTimeField(blank=True, null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Новость'
|
||||
verbose_name_plural = 'Новости'
|
||||
ordering = ['-published_date', '-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
base = slugify(self.title)[:200]
|
||||
slug = base
|
||||
i = 1
|
||||
while NewsArticle.objects.filter(slug=slug).exists():
|
||||
slug = f"{base}-{i}"
|
||||
i += 1
|
||||
self.slug = slug
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class CareerVacancy(models.Model):
|
||||
FULL_TIME = 'FT'
|
||||
PART_TIME = 'PT'
|
||||
CONTRACT = 'CT'
|
||||
INTERN = 'IN'
|
||||
EMPLOYMENT_CHOICES = [
|
||||
(FULL_TIME, 'Полная занятость'),
|
||||
(PART_TIME, 'Частичная занятость'),
|
||||
(CONTRACT, 'Контракт'),
|
||||
(INTERN, 'Стажировка'),
|
||||
]
|
||||
|
||||
title = models.CharField(max_length=200)
|
||||
slug = models.SlugField(max_length=220, unique=True, blank=True)
|
||||
location = models.CharField(max_length=200, blank=True)
|
||||
employment_type = models.CharField(max_length=2, choices=EMPLOYMENT_CHOICES, default=FULL_TIME)
|
||||
responsibilities = models.TextField()
|
||||
requirements = models.TextField()
|
||||
desirable = models.TextField(blank=True)
|
||||
salary_min = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
salary_max = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
posted_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Вакансия'
|
||||
verbose_name_plural = 'Вакансии'
|
||||
ordering = ['-posted_at']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
base = slugify(self.title)[:200]
|
||||
slug = base
|
||||
i = 1
|
||||
while CareerVacancy.objects.filter(slug=slug).exists():
|
||||
slug = f"{base}-{i}"
|
||||
i += 1
|
||||
self.slug = slug
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class FooterSettings(models.Model):
|
||||
"""Настройки футера - все данные управляются из админки"""
|
||||
|
||||
@@ -374,7 +547,12 @@ class TeamMember(models.Model):
|
||||
# Social Links
|
||||
email = models.EmailField(blank=True, verbose_name='Email')
|
||||
phone = models.CharField(max_length=50, blank=True, verbose_name='Телефон')
|
||||
telegram = models.CharField(max_length=100, blank=True, verbose_name='Telegram')
|
||||
telegram = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
verbose_name='Telegram',
|
||||
help_text='Username без @ (например: trevor1985)'
|
||||
)
|
||||
linkedin = models.URLField(blank=True, verbose_name='LinkedIn')
|
||||
github = models.URLField(blank=True, verbose_name='GitHub')
|
||||
|
||||
|
||||
@@ -180,108 +180,8 @@
|
||||
</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>
|
||||
<!-- Team Section - Dynamic from Database -->
|
||||
{% include 'web/team_section.html' %}
|
||||
|
||||
<!-- Technologies Section -->
|
||||
<section class="section-padding bg-light">
|
||||
|
||||
50
smartsoltech/web/templates/web/blog_detail.html
Normal file
50
smartsoltech/web/templates/web/blog_detail.html
Normal file
@@ -0,0 +1,50 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ post.title }} - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'blog_list' %}">Блог</a></li>
|
||||
<li class="breadcrumb-item active">{{ post.title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Post Header -->
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-5 fw-bold mb-3">{{ post.title }}</h1>
|
||||
<div class="d-flex justify-content-center align-items-center gap-3 text-muted">
|
||||
<span><i class="far fa-calendar"></i> {{ post.published_date|date:"d.m.Y" }}</span>
|
||||
{% if post.author %}
|
||||
<span><i class="far fa-user"></i> {{ post.author.username }}</span>
|
||||
{% endif %}
|
||||
<span><i class="far fa-eye"></i> {{ post.views }} просмотров</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post Image -->
|
||||
{% if post.image %}
|
||||
<div class="mb-5">
|
||||
<img src="{{ post.image.url }}" class="img-fluid rounded" alt="{{ post.title }}" style="width: 100%; max-height: 500px; object-fit: cover;">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Post Content -->
|
||||
<div class="post-content mb-5">
|
||||
{{ post.content|linebreaks }}
|
||||
</div>
|
||||
|
||||
<!-- Back to Blog -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'blog_list' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> Вернуться к блогу
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
71
smartsoltech/web/templates/web/blog_list.html
Normal file
71
smartsoltech/web/templates/web/blog_list.html
Normal file
@@ -0,0 +1,71 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Блог - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<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">
|
||||
{% if posts %}
|
||||
{% for post in posts %}
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-modern h-100">
|
||||
{% if post.image %}
|
||||
<img src="{{ post.image.url }}" class="card-img-top" alt="{{ post.title }}" style="height: 200px; object-fit: cover;">
|
||||
{% else %}
|
||||
<div class="card-img-top bg-gradient-primary d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-blog fa-3x text-white"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> {{ post.published_date|date:"d.m.Y" }}
|
||||
</small>
|
||||
{% if post.author %}
|
||||
<small class="text-muted">
|
||||
<i class="far fa-user"></i> {{ post.author.username }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h5 class="card-title">{{ post.title }}</h5>
|
||||
{% if post.excerpt %}
|
||||
<p class="card-text text-muted">{{ post.excerpt|truncatewords:20 }}</p>
|
||||
{% else %}
|
||||
<p class="card-text text-muted">{{ post.content|truncatewords:20|striptags }}</p>
|
||||
{% endif %}
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="{% url 'blog_detail' post.slug %}" class="btn btn-primary btn-sm">
|
||||
Читать далее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
<small class="text-muted">
|
||||
<i class="far fa-eye"></i> {{ post.views }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-inbox fa-4x text-muted mb-3"></i>
|
||||
<p class="text-muted">Пока нет опубликованных постов</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
96
smartsoltech/web/templates/web/career_detail.html
Normal file
96
smartsoltech/web/templates/web/career_detail.html
Normal file
@@ -0,0 +1,96 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ vacancy.title }} - Вакансии - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 900px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'career_list' %}">Вакансии</a></li>
|
||||
<li class="breadcrumb-item active">{{ vacancy.title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Vacancy Header -->
|
||||
<div class="mb-5">
|
||||
<h1 class="display-5 fw-bold mb-3">{{ vacancy.title }}</h1>
|
||||
<div class="d-flex flex-wrap gap-3 mb-3">
|
||||
{% if vacancy.location %}
|
||||
<span class="text-muted">
|
||||
<i class="fas fa-map-marker-alt"></i> {{ vacancy.location }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-primary">
|
||||
{{ vacancy.get_employment_type_display }}
|
||||
</span>
|
||||
<span class="text-muted">
|
||||
<i class="far fa-calendar"></i> Опубликовано: {{ vacancy.posted_at|date:"d.m.Y" }}
|
||||
</span>
|
||||
</div>
|
||||
{% if vacancy.salary_min or vacancy.salary_max %}
|
||||
<div class="mb-3">
|
||||
<strong class="text-primary h4">
|
||||
{% if vacancy.salary_min and vacancy.salary_max %}
|
||||
${{ vacancy.salary_min|floatformat:0 }} - ${{ vacancy.salary_max|floatformat:0 }}
|
||||
{% elif vacancy.salary_min %}
|
||||
От ${{ vacancy.salary_min|floatformat:0 }}
|
||||
{% else %}
|
||||
До ${{ vacancy.salary_max|floatformat:0 }}
|
||||
{% endif %}
|
||||
</strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Responsibilities -->
|
||||
<div class="card-modern mb-4">
|
||||
<div class="card-body">
|
||||
<h3 class="mb-4"><i class="fas fa-tasks text-primary me-2"></i> Обязанности</h3>
|
||||
<div class="vacancy-content">
|
||||
{{ vacancy.responsibilities|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Requirements -->
|
||||
<div class="card-modern mb-4">
|
||||
<div class="card-body">
|
||||
<h3 class="mb-4"><i class="fas fa-check-circle text-success me-2"></i> Требования</h3>
|
||||
<div class="vacancy-content">
|
||||
{{ vacancy.requirements|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desirable -->
|
||||
{% if vacancy.desirable %}
|
||||
<div class="card-modern mb-4">
|
||||
<div class="card-body">
|
||||
<h3 class="mb-4"><i class="fas fa-star text-warning me-2"></i> Будет плюсом</h3>
|
||||
<div class="vacancy-content">
|
||||
{{ vacancy.desirable|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Apply Button -->
|
||||
<div class="text-center mt-5 mb-4">
|
||||
<a href="mailto:hr@smartsoltech.kr?subject=Отклик на вакансию: {{ vacancy.title }}" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-paper-plane me-2"></i> Откликнуться на вакансию
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Back to Careers -->
|
||||
<div class="text-center mt-4">
|
||||
<a href="{% url 'career_list' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> Все вакансии
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
77
smartsoltech/web/templates/web/career_list.html
Normal file
77
smartsoltech/web/templates/web/career_list.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Вакансии - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<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 justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
{% if vacancies %}
|
||||
{% for vacancy in vacancies %}
|
||||
<div class="card-modern mb-4" data-aos="fade-up" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4 class="mb-2">{{ vacancy.title }}</h4>
|
||||
<div class="d-flex gap-3 mb-3">
|
||||
{% if vacancy.location %}
|
||||
<span class="text-muted">
|
||||
<i class="fas fa-map-marker-alt"></i> {{ vacancy.location }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-primary">
|
||||
{{ vacancy.get_employment_type_display }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-muted mb-0">
|
||||
{{ vacancy.responsibilities|truncatewords:30|striptags }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-md-end d-flex flex-column justify-content-center">
|
||||
{% if vacancy.salary_min or vacancy.salary_max %}
|
||||
<div class="mb-3">
|
||||
<strong class="text-primary">
|
||||
{% if vacancy.salary_min and vacancy.salary_max %}
|
||||
${{ vacancy.salary_min|floatformat:0 }} - ${{ vacancy.salary_max|floatformat:0 }}
|
||||
{% elif vacancy.salary_min %}
|
||||
От ${{ vacancy.salary_min|floatformat:0 }}
|
||||
{% else %}
|
||||
До ${{ vacancy.salary_max|floatformat:0 }}
|
||||
{% endif %}
|
||||
</strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'career_detail' vacancy.slug %}" class="btn btn-primary">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-briefcase fa-4x text-muted mb-3"></i>
|
||||
<h4 class="text-muted">В данный момент нет открытых вакансий</h4>
|
||||
<p class="text-muted">Следите за обновлениями или отправьте инициативное резюме</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -66,20 +66,30 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="#" class="text-light opacity-75 text-decoration-none hover-primary">
|
||||
<a href="{% url 'portfolio_list' %}" 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 href="{% url 'about' %}#team" 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 href="{% url 'career_list' %}" class="text-light opacity-75 text-decoration-none hover-primary">
|
||||
Карьера
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="{% url 'blog_list' %}" class="text-light opacity-75 text-decoration-none hover-primary">
|
||||
Блог
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="{% url 'news_list' %}" class="text-light opacity-75 text-decoration-none hover-primary">
|
||||
Новости
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -134,12 +144,12 @@
|
||||
<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 href="{% url 'privacy_policy' %}" 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 href="{% url 'terms_of_use' %}" class="text-light opacity-75 text-decoration-none hover-primary small">
|
||||
Условия использования
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -256,6 +256,316 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Portfolio 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">работы</span>
|
||||
</h2>
|
||||
<p class="lead text-muted max-width-600 mx-auto">
|
||||
Избранные проекты, которыми мы гордимся
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4" id="portfolio-preview">
|
||||
{% if featured_portfolio %}
|
||||
{% for item in featured_portfolio %}
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-modern h-100 overflow-hidden">
|
||||
{% if item.image %}
|
||||
<img src="{{ item.image.url }}" class="card-img-top" alt="{{ item.title }}" style="height: 200px; object-fit: cover;">
|
||||
{% else %}
|
||||
<div class="card-img-top bg-gradient-primary d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-briefcase fa-3x text-white"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
{% if item.category %}
|
||||
<span class="badge bg-primary mb-2">{{ item.category.name }}</span>
|
||||
{% endif %}
|
||||
<h5 class="card-title">{{ item.title }}</h5>
|
||||
<p class="card-text text-muted">{{ item.description|truncatewords:10 }}</p>
|
||||
<a href="{% url 'portfolio_detail' item.slug %}" class="btn btn-outline-primary btn-sm">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<!-- Fallback content if no portfolio items -->
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up">
|
||||
<div class="card-modern h-100 overflow-hidden">
|
||||
<div class="card-img-top bg-gradient-primary d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-briefcase fa-3x text-white"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="badge bg-primary mb-2">Веб-разработка</span>
|
||||
<h5 class="card-title">Корпоративный сайт</h5>
|
||||
<p class="card-text text-muted">Современный корпоративный сайт с CMS</p>
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-outline-primary btn-sm">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="card-modern h-100 overflow-hidden">
|
||||
<div class="card-img-top bg-gradient-accent d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-mobile-alt fa-3x text-white"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="badge bg-success mb-2">Мобильные приложения</span>
|
||||
<h5 class="card-title">E-commerce приложение</h5>
|
||||
<p class="card-text text-muted">Мобильное приложение для онлайн-торговли</p>
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-outline-primary btn-sm">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="200">
|
||||
<div class="card-modern h-100 overflow-hidden">
|
||||
<div class="card-img-top bg-gradient-success d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-chart-line fa-3x text-white"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="badge bg-warning mb-2">Аналитика</span>
|
||||
<h5 class="card-title">Система аналитики</h5>
|
||||
<p class="card-text text-muted">Платформа для бизнес-аналитики</p>
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-outline-primary btn-sm">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-primary-modern btn-lg">
|
||||
Смотреть все проекты <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Blog & News Section -->
|
||||
<section class="section-padding">
|
||||
<div class="container-modern">
|
||||
<div class="row g-5">
|
||||
<!-- Latest Blog Posts -->
|
||||
<div class="col-lg-6">
|
||||
<div class="mb-4">
|
||||
<span class="badge bg-primary mb-2">📝 Блог</span>
|
||||
<h3 class="fw-bold mb-3">Последние статьи</h3>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% if recent_blog_posts %}
|
||||
{% for post in recent_blog_posts %}
|
||||
<div class="card-modern border-start border-primary border-4" data-aos="fade-right" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> {{ post.published_date|date:"d.m.Y" }}
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">{{ post.title }}</h6>
|
||||
<p class="small text-muted mb-2">
|
||||
{% if post.excerpt %}
|
||||
{{ post.excerpt|truncatewords:15 }}
|
||||
{% else %}
|
||||
{{ post.content|truncatewords:15|striptags }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<a href="{% url 'blog_detail' post.slug %}" class="text-primary text-decoration-none small">
|
||||
Читать далее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="card-modern border-start border-primary border-4" data-aos="fade-right">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> 20 ноября 2025
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">Тренды веб-разработки 2025</h6>
|
||||
<p class="small text-muted mb-2">Обзор главных технологий и подходов в современной веб-разработке...</p>
|
||||
<a href="{% url 'blog_list' %}" class="text-primary text-decoration-none small">
|
||||
Читать далее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-modern border-start border-primary border-4" data-aos="fade-right" data-aos-delay="100">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> 15 ноября 2025
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">Как выбрать стек технологий</h6>
|
||||
<p class="small text-muted mb-2">Практические советы по выбору технологий для вашего проекта...</p>
|
||||
<a href="{% url 'blog_list' %}" class="text-primary text-decoration-none small">
|
||||
Читать далее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'blog_list' %}" class="btn btn-outline-primary">
|
||||
Все статьи <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Latest News -->
|
||||
<div class="col-lg-6">
|
||||
<div class="mb-4">
|
||||
<span class="badge bg-success mb-2">📰 Новости</span>
|
||||
<h3 class="fw-bold mb-3">Последние новости</h3>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-3">
|
||||
{% if recent_news %}
|
||||
{% for article in recent_news %}
|
||||
<div class="card-modern border-start border-success border-4" data-aos="fade-left" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> {{ article.published_date|date:"d.m.Y" }}
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">{{ article.title }}</h6>
|
||||
<p class="small text-muted mb-2">
|
||||
{% if article.excerpt %}
|
||||
{{ article.excerpt|truncatewords:15 }}
|
||||
{% else %}
|
||||
{{ article.content|truncatewords:15|striptags }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<a href="{% url 'news_detail' article.slug %}" class="text-success text-decoration-none small">
|
||||
Узнать больше <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="card-modern border-start border-success border-4" data-aos="fade-left">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> 22 ноября 2025
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">Новый проект запущен</h6>
|
||||
<p class="small text-muted mb-2">Мы рады объявить о запуске нового масштабного проекта...</p>
|
||||
<a href="{% url 'news_list' %}" class="text-success text-decoration-none small">
|
||||
Узнать больше <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-modern border-start border-success border-4" data-aos="fade-left" data-aos-delay="100">
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> 18 ноября 2025
|
||||
</small>
|
||||
<h6 class="mt-2 mb-2">Расширение команды</h6>
|
||||
<p class="small text-muted mb-2">SmartSolTech открывает новые вакансии для талантливых специалистов...</p>
|
||||
<a href="{% url 'news_list' %}" class="text-success text-decoration-none small">
|
||||
Узнать больше <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'news_list' %}" class="btn btn-outline-success">
|
||||
Все новости <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Career Section -->
|
||||
<section class="section-padding bg-light">
|
||||
<div class="container-modern">
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-6" data-aos="fade-right">
|
||||
<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">нашей команде</span>
|
||||
</h2>
|
||||
<p class="lead text-muted mb-4">
|
||||
Мы ищем талантливых специалистов, которые разделяют нашу страсть к технологиям и инновациям.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-column gap-3 mb-4">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="feature-icon bg-primary text-white rounded-3 me-3">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-1">Профессиональный рост</h6>
|
||||
<p class="text-muted small mb-0">Возможности для развития и обучения</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="feature-icon bg-success text-white rounded-3 me-3">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-1">Команда профессионалов</h6>
|
||||
<p class="text-muted small mb-0">Работайте с лучшими специалистами</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="feature-icon bg-warning text-white rounded-3 me-3">
|
||||
<i class="fas fa-laptop-house"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-1">Гибкий график</h6>
|
||||
<p class="text-muted small mb-0">Удаленная работа и гибкое расписание</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{% url 'career_list' %}" class="btn btn-primary-modern btn-lg">
|
||||
Смотреть вакансии <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6" data-aos="fade-left">
|
||||
<div class="position-relative">
|
||||
<div class="card-modern p-5 text-center bg-gradient text-white">
|
||||
<i class="fas fa-briefcase fa-5x mb-4 opacity-50"></i>
|
||||
<h4 class="fw-bold mb-3">Открыто вакансий</h4>
|
||||
<div class="display-4 fw-bold mb-3">
|
||||
{% if active_vacancies_count > 0 %}
|
||||
{{ active_vacancies_count }}+
|
||||
{% else %}
|
||||
0
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="mb-4 opacity-90">Найдите свою идеальную позицию</p>
|
||||
<a href="{% url 'career_list' %}" class="btn btn-light btn-lg">
|
||||
Посмотреть все
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="section-padding bg-gradient text-white">
|
||||
<div class="container-modern text-center">
|
||||
|
||||
47
smartsoltech/web/templates/web/news_detail.html
Normal file
47
smartsoltech/web/templates/web/news_detail.html
Normal file
@@ -0,0 +1,47 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ article.title }} - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'news_list' %}">Новости</a></li>
|
||||
<li class="breadcrumb-item active">{{ article.title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Article Header -->
|
||||
<div class="text-center mb-5">
|
||||
<span class="badge bg-primary mb-3">Новость</span>
|
||||
<h1 class="display-5 fw-bold mb-3">{{ article.title }}</h1>
|
||||
<div class="text-muted">
|
||||
<i class="far fa-calendar"></i> {{ article.published_date|date:"d.m.Y H:i" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Article Image -->
|
||||
{% if article.image %}
|
||||
<div class="mb-5">
|
||||
<img src="{{ article.image.url }}" class="img-fluid rounded" alt="{{ article.title }}" style="width: 100%; max-height: 500px; object-fit: cover;">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Article Content -->
|
||||
<div class="article-content mb-5">
|
||||
{{ article.content|linebreaks }}
|
||||
</div>
|
||||
|
||||
<!-- Back to News -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'news_list' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> Все новости
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
59
smartsoltech/web/templates/web/news_list.html
Normal file
59
smartsoltech/web/templates/web/news_list.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Новости - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<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">
|
||||
{% if news %}
|
||||
{% for article in news %}
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-modern h-100">
|
||||
{% if article.image %}
|
||||
<img src="{{ article.image.url }}" class="card-img-top" alt="{{ article.title }}" style="height: 200px; object-fit: cover;">
|
||||
{% else %}
|
||||
<div class="card-img-top bg-gradient-accent d-flex align-items-center justify-content-center" style="height: 200px;">
|
||||
<i class="fas fa-newspaper fa-3x text-white"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar"></i> {{ article.published_date|date:"d.m.Y H:i" }}
|
||||
</small>
|
||||
<h5 class="card-title mt-2">{{ article.title }}</h5>
|
||||
{% if article.excerpt %}
|
||||
<p class="card-text text-muted">{{ article.excerpt }}</p>
|
||||
{% else %}
|
||||
<p class="card-text text-muted">{{ article.content|truncatewords:20|striptags }}</p>
|
||||
{% endif %}
|
||||
<a href="{% url 'news_detail' article.slug %}" class="btn btn-primary btn-sm">
|
||||
Читать далее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-inbox fa-4x text-muted mb-3"></i>
|
||||
<p class="text-muted">Пока нет новостей</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
61
smartsoltech/web/templates/web/portfolio_detail.html
Normal file
61
smartsoltech/web/templates/web/portfolio_detail.html
Normal file
@@ -0,0 +1,61 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ item.title }} - Портфолио - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 1000px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'portfolio_list' %}">Портфолио</a></li>
|
||||
<li class="breadcrumb-item active">{{ item.title }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Project Header -->
|
||||
<div class="text-center mb-5">
|
||||
{% if item.category %}
|
||||
<span class="badge bg-primary mb-3">{{ item.category.name }}</span>
|
||||
{% endif %}
|
||||
<h1 class="display-5 fw-bold mb-3">{{ item.title }}</h1>
|
||||
{% if item.client_name %}
|
||||
<p class="text-muted">
|
||||
<i class="fas fa-user"></i> Клиент: {{ item.client_name }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if item.completion_date %}
|
||||
<p class="text-muted">
|
||||
<i class="far fa-calendar"></i> Дата завершения: {{ item.completion_date|date:"d.m.Y" }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Project Image -->
|
||||
{% if item.image %}
|
||||
<div class="mb-5">
|
||||
<img src="{{ item.image.url }}" class="img-fluid rounded" alt="{{ item.title }}" style="width: 100%; max-height: 600px; object-fit: cover;">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Project Description -->
|
||||
<div class="card-modern mb-5">
|
||||
<div class="card-body">
|
||||
<h3 class="mb-4">О проекте</h3>
|
||||
<div class="project-description">
|
||||
{{ item.description|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Back to Portfolio -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> Все проекты
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
83
smartsoltech/web/templates/web/portfolio_list.html
Normal file
83
smartsoltech/web/templates/web/portfolio_list.html
Normal file
@@ -0,0 +1,83 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Портфолио - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<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>
|
||||
|
||||
<!-- Category Filters -->
|
||||
{% if categories %}
|
||||
<div class="text-center mb-4">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'portfolio_list' %}" class="btn btn-outline-primary {% if not request.GET.category %}active{% endif %}">
|
||||
Все
|
||||
</a>
|
||||
{% for category in categories %}
|
||||
<a href="?category={{ category.id }}" class="btn btn-outline-primary {% if request.GET.category == category.id|stringformat:'s' %}active{% endif %}">
|
||||
{{ category.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row g-4">
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<div class="col-lg-4 col-md-6" data-aos="fade-up" data-aos-delay="{{ forloop.counter0|add:'00' }}">
|
||||
<div class="card-modern h-100 overflow-hidden">
|
||||
{% if item.image %}
|
||||
<img src="{{ item.image.url }}" class="card-img-top" alt="{{ item.title }}" style="height: 250px; object-fit: cover;">
|
||||
{% else %}
|
||||
<div class="card-img-top bg-gradient-primary d-flex align-items-center justify-content-center" style="height: 250px;">
|
||||
<i class="fas fa-briefcase fa-3x text-white"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
{% if item.category %}
|
||||
<span class="badge bg-primary mb-2">{{ item.category.name }}</span>
|
||||
{% endif %}
|
||||
<h5 class="card-title">{{ item.title }}</h5>
|
||||
{% if item.client_name %}
|
||||
<p class="text-muted small mb-2">
|
||||
<i class="fas fa-user"></i> {{ item.client_name }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="card-text text-muted">{{ item.description|truncatewords:15 }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="{% url 'portfolio_detail' item.slug %}" class="btn btn-primary btn-sm">
|
||||
Подробнее <i class="fas fa-arrow-right ms-1"></i>
|
||||
</a>
|
||||
{% if item.completion_date %}
|
||||
<small class="text-muted">
|
||||
{{ item.completion_date|date:"Y" }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<i class="fas fa-inbox fa-4x text-muted mb-3"></i>
|
||||
<p class="text-muted">Портфолио пусто</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
63
smartsoltech/web/templates/web/privacy_policy.html
Normal file
63
smartsoltech/web/templates/web/privacy_policy.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Политика конфиденциальности - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 900px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item active">Политика конфиденциальности</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
{% if policy %}
|
||||
<!-- Header -->
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-5 fw-bold mb-3">Политика конфиденциальности</h1>
|
||||
<p class="text-muted">
|
||||
Версия {{ policy.version }} | Действует с {{ policy.effective_date|date:"d.m.Y" }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="card-modern">
|
||||
<div class="card-body">
|
||||
<div class="legal-content">
|
||||
{{ policy.content|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-file-alt fa-4x text-muted mb-3"></i>
|
||||
<h4 class="text-muted">Политика конфиденциальности не найдена</h4>
|
||||
<p class="text-muted">Содержимое будет добавлено в ближайшее время</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Back Button -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'home' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> На главную
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.legal-content {
|
||||
line-height: 1.8;
|
||||
}
|
||||
.legal-content h2, .legal-content h3, .legal-content h4 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.legal-content ul, .legal-content ol {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -65,7 +65,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if member.telegram %}
|
||||
<a href="https://t.me/{{ member.telegram }}" target="_blank" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
|
||||
<a href="tg://resolve?domain={{ member.telegram }}" title="Открыть в Telegram: @{{ member.telegram }}" class="btn btn-outline-primary btn-sm rounded-circle" style="width: 40px; height: 40px;">
|
||||
<i class="fab fa-telegram-plane"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
63
smartsoltech/web/templates/web/terms_of_use.html
Normal file
63
smartsoltech/web/templates/web/terms_of_use.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends 'web/base_modern.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Условия использования - SmartSolTech{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section-padding">
|
||||
<div class="container" style="max-width: 900px;">
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'home' %}">Главная</a></li>
|
||||
<li class="breadcrumb-item active">Условия использования</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
{% if terms %}
|
||||
<!-- Header -->
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-5 fw-bold mb-3">Условия использования</h1>
|
||||
<p class="text-muted">
|
||||
Версия {{ terms.version }} | Действует с {{ terms.effective_date|date:"d.m.Y" }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="card-modern">
|
||||
<div class="card-body">
|
||||
<div class="legal-content">
|
||||
{{ terms.content|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-file-contract fa-4x text-muted mb-3"></i>
|
||||
<h4 class="text-muted">Условия использования не найдены</h4>
|
||||
<p class="text-muted">Содержимое будет добавлено в ближайшее время</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Back Button -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="{% url 'home' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i> На главную
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.legal-content {
|
||||
line-height: 1.8;
|
||||
}
|
||||
.legal-content h2, .legal-content h3, .legal-content h4 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.legal-content ul, .legal-content ol {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -24,6 +24,26 @@ urlpatterns = [
|
||||
# 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'),
|
||||
|
||||
# Blog
|
||||
path('blog/', views.blog_list, name='blog_list'),
|
||||
path('blog/<slug:slug>/', views.blog_detail, name='blog_detail'),
|
||||
|
||||
# News
|
||||
path('news/', views.news_list, name='news_list'),
|
||||
path('news/<slug:slug>/', views.news_detail, name='news_detail'),
|
||||
|
||||
# Portfolio
|
||||
path('portfolio/', views.portfolio_list, name='portfolio_list'),
|
||||
path('portfolio/<slug:slug>/', views.portfolio_detail, name='portfolio_detail'),
|
||||
|
||||
# Career
|
||||
path('career/', views.career_list, name='career_list'),
|
||||
path('career/<slug:slug>/', views.career_detail, name='career_detail'),
|
||||
|
||||
# Legal pages
|
||||
path('privacy/', views.privacy_policy, name='privacy_policy'),
|
||||
path('terms/', views.terms_of_use, name='terms_of_use'),
|
||||
]
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
@@ -1,5 +1,9 @@
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, Category, AboutPage, FooterSettings, TeamMember
|
||||
from .models import (
|
||||
Service, Project, Client, BlogPost, Review, Order, ServiceRequest,
|
||||
Category, AboutPage, FooterSettings, TeamMember,
|
||||
NewsArticle, CareerVacancy, PortfolioItem, PrivacyPolicy, TermsOfUse
|
||||
)
|
||||
from django.db.models import Avg
|
||||
from comunication.models import TelegramSettings
|
||||
import qrcode
|
||||
@@ -36,7 +40,28 @@ except Exception as e:
|
||||
|
||||
def home(request):
|
||||
services = Service.objects.all()[:6] # Показываем только первые 6 услуг на главной
|
||||
return render(request, 'web/home_modern.html', {'services': services})
|
||||
|
||||
# Последние посты блога
|
||||
recent_blog_posts = BlogPost.objects.filter(status=BlogPost.PUBLISHED).order_by('-published_date')[:2]
|
||||
|
||||
# Последние новости
|
||||
recent_news = NewsArticle.objects.filter(is_published=True).order_by('-published_date')[:2]
|
||||
|
||||
# Избранные проекты портфолио
|
||||
featured_portfolio = PortfolioItem.objects.filter(featured=True, is_active=True)[:3]
|
||||
|
||||
# Количество активных вакансий
|
||||
active_vacancies_count = CareerVacancy.objects.filter(is_active=True).count()
|
||||
|
||||
context = {
|
||||
'services': services,
|
||||
'recent_blog_posts': recent_blog_posts,
|
||||
'recent_news': recent_news,
|
||||
'featured_portfolio': featured_portfolio,
|
||||
'active_vacancies_count': active_vacancies_count,
|
||||
}
|
||||
|
||||
return render(request, 'web/home_modern.html', context)
|
||||
|
||||
def service_detail(request, pk):
|
||||
service = get_object_or_404(Service, pk=pk)
|
||||
@@ -353,3 +378,79 @@ def check_request_status(request, request_id):
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при проверке статуса заявки {request_id}: {str(e)}")
|
||||
return JsonResponse({'error': 'Ошибка сервера'}, status=500)
|
||||
|
||||
|
||||
# ========== Blog Views ==========
|
||||
def blog_list(request):
|
||||
"""Список всех опубликованных постов блога"""
|
||||
posts = BlogPost.objects.filter(status=BlogPost.PUBLISHED).order_by('-published_date')
|
||||
return render(request, 'web/blog_list.html', {'posts': posts})
|
||||
|
||||
|
||||
def blog_detail(request, slug):
|
||||
"""Детальная страница поста блога"""
|
||||
post = get_object_or_404(BlogPost, slug=slug, status=BlogPost.PUBLISHED)
|
||||
post.views += 1
|
||||
post.save(update_fields=['views'])
|
||||
return render(request, 'web/blog_detail.html', {'post': post})
|
||||
|
||||
|
||||
# ========== News Views ==========
|
||||
def news_list(request):
|
||||
"""Список всех опубликованных новостей"""
|
||||
news = NewsArticle.objects.filter(is_published=True).order_by('-published_date')
|
||||
return render(request, 'web/news_list.html', {'news': news})
|
||||
|
||||
|
||||
def news_detail(request, slug):
|
||||
"""Детальная страница новости"""
|
||||
article = get_object_or_404(NewsArticle, slug=slug, is_published=True)
|
||||
return render(request, 'web/news_detail.html', {'article': article})
|
||||
|
||||
|
||||
# ========== Portfolio Views ==========
|
||||
def portfolio_list(request):
|
||||
"""Список всех активных элементов портфолио"""
|
||||
category_id = request.GET.get('category')
|
||||
items = PortfolioItem.objects.filter(is_active=True)
|
||||
|
||||
if category_id:
|
||||
items = items.filter(category_id=category_id)
|
||||
|
||||
categories = Category.objects.all()
|
||||
return render(request, 'web/portfolio_list.html', {
|
||||
'items': items,
|
||||
'categories': categories
|
||||
})
|
||||
|
||||
|
||||
def portfolio_detail(request, slug):
|
||||
"""Детальная страница элемента портфолио"""
|
||||
item = get_object_or_404(PortfolioItem, slug=slug, is_active=True)
|
||||
return render(request, 'web/portfolio_detail.html', {'item': item})
|
||||
|
||||
|
||||
# ========== Career Views ==========
|
||||
def career_list(request):
|
||||
"""Список всех активных вакансий"""
|
||||
vacancies = CareerVacancy.objects.filter(is_active=True).order_by('-posted_at')
|
||||
return render(request, 'web/career_list.html', {'vacancies': vacancies})
|
||||
|
||||
|
||||
def career_detail(request, slug):
|
||||
"""Детальная страница вакансии"""
|
||||
vacancy = get_object_or_404(CareerVacancy, slug=slug, is_active=True)
|
||||
return render(request, 'web/career_detail.html', {'vacancy': vacancy})
|
||||
|
||||
|
||||
# ========== Legal Pages Views ==========
|
||||
def privacy_policy(request):
|
||||
"""Страница политики конфиденциальности"""
|
||||
policy = PrivacyPolicy.objects.filter(is_active=True).first()
|
||||
return render(request, 'web/privacy_policy.html', {'policy': policy})
|
||||
|
||||
|
||||
def terms_of_use(request):
|
||||
"""Страница условий использования"""
|
||||
terms = TermsOfUse.objects.filter(is_active=True).first()
|
||||
return render(request, 'web/terms_of_use.html', {'terms': terms})
|
||||
|
||||
Reference in New Issue
Block a user