Compare commits

...

2 Commits

Author SHA1 Message Date
9839389fc9 🎨 Добавлен фронтенд для команды и карьеры
Some checks failed
continuous-integration/drone/push Build is failing
 Новые страницы:
- 👥 /team/ - Страница команды с детальной информацией
  • Красивые карточки сотрудников с фото
  • Социальные сети и контактные данные
  • Навыки и опыт работы
  • Адаптивный дизайн с hover эффектами

- 💼 /career/ - Страница карьеры с вакансиями
  • Рекомендуемые и обычные вакансии
  • Фильтры по отделам
  • Детальная информация о позициях
  • Зарплатные вилки и требования
  • Интеграция с email для откликов

🏠 Главная страница:
- Интеграция секций команды и карьеры
- Гармоничное размещение между существующими блоками
- Топ-4 сотрудника и топ-3 вакансии
- Кнопки перехода на полные страницы

🧭 Навигация:
- Добавлены ссылки 'Команда' и 'Карьера' в меню
- Активные состояния для новых страниц
- Адаптивная навигация для мобильных

🎯 UX/UI улучшения:
- Современный градиентный дизайн
- Анимации и hover эффекты
- Skill tags и бейджи
- Responsive дизайн для всех устройств
- Интеграция с социальными сетями
2025-11-25 17:09:15 +09:00
ec01a2ae10 👥 Добавлено управление персоналом и карьерой
 Новые функции:
- 🧑‍💻 Team модель для управления сотрудниками
  • Полная информация о персонале (имя, должность, отдел)
  • Фотографии и контактные данные
  • Социальные сети (LinkedIn, GitHub, Telegram)
  • Навыки и опыт работы
  • Гибкие настройки отображения

- 💼 Career модель для вакансий
  • Детальное описание позиций
  • Требования и обязанности
  • Зарплатные вилки
  • Типы занятости и уровни опыта
  • Статусы вакансий и дедлайны

🔧 Админ-панель:
- Удобные интерфейсы для HR-менеджмента
- Группировка полей и фильтрация
- Быстрые действия для массовых операций
- Сортировка по приоритету

📊 База данных:
- Миграция 0013_career_team.py
- Оптимизированные индексы и связи
2025-11-25 15:44:57 +09:00
10 changed files with 1775 additions and 3 deletions

View File

@@ -1,5 +1,5 @@
from django.contrib import admin
from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest, HeroBanner, ContactInfo
from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest, HeroBanner, ContactInfo, Team, Career
from .forms import ProjectForm
@admin.register(ContactInfo)
@@ -88,3 +88,95 @@ class ServiceRequestAdmin(admin.ModelAdmin):
list_display = ('service','token', 'client', 'created_at')
search_fields = ('service','token', 'client')
list_filter = ('service','token','client')
@admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'position', 'department', 'is_active', 'display_order')
list_filter = ('department', 'is_active', 'show_on_about')
search_fields = ('first_name', 'last_name', 'position', 'skills')
list_editable = ('display_order', 'is_active')
fieldsets = (
('Основная информация', {
'fields': ('first_name', 'last_name', 'position', 'department')
}),
('Контактные данные', {
'fields': ('email', 'phone', 'photo'),
'classes': ('collapse',)
}),
('Профессиональная информация', {
'fields': ('bio', 'skills', 'experience_years')
}),
('Социальные сети', {
'fields': ('linkedin', 'github', 'telegram'),
'classes': ('collapse',)
}),
('Настройки отображения', {
'fields': ('is_active', 'show_on_about', 'display_order')
}),
)
def get_queryset(self, request):
return super().get_queryset(request).order_by('display_order', 'last_name')
@admin.register(Career)
class CareerAdmin(admin.ModelAdmin):
list_display = ('title', 'department', 'experience_level', 'employment_type', 'status', 'is_featured', 'created_at')
list_filter = ('status', 'employment_type', 'experience_level', 'department', 'is_featured')
search_fields = ('title', 'department', 'description', 'required_skills')
list_editable = ('status', 'is_featured')
fieldsets = (
('Основная информация', {
'fields': ('title', 'department', 'location', 'employment_type', 'experience_level')
}),
('Описание вакансии', {
'fields': ('description', 'responsibilities', 'requirements', 'benefits')
}),
('Зарплата', {
'fields': ('salary_min', 'salary_max', 'salary_currency'),
'classes': ('collapse',)
}),
('Навыки', {
'fields': ('required_skills', 'preferred_skills'),
}),
('Контактная информация', {
'fields': ('contact_email', 'contact_person'),
'classes': ('collapse',)
}),
('Статус и метаданные', {
'fields': ('status', 'is_featured', 'application_deadline', 'published_at')
}),
)
readonly_fields = ('created_at', 'updated_at')
def get_queryset(self, request):
return super().get_queryset(request).order_by('-is_featured', '-created_at')
def save_model(self, request, obj, form, change):
if obj.status == 'active' and not obj.published_at:
from django.utils import timezone
obj.published_at = timezone.now()
super().save_model(request, obj, form, change)
actions = ['mark_as_active', 'mark_as_paused', 'mark_as_closed']
def mark_as_active(self, request, queryset):
from django.utils import timezone
updated = queryset.update(status='active')
queryset.filter(published_at__isnull=True).update(published_at=timezone.now())
self.message_user(request, f'{updated} вакансий отмечены как активные.')
mark_as_active.short_description = "Отметить как активные"
def mark_as_paused(self, request, queryset):
updated = queryset.update(status='paused')
self.message_user(request, f'{updated} вакансий приостановлены.')
mark_as_paused.short_description = "Приостановить"
def mark_as_closed(self, request, queryset):
updated = queryset.update(status='closed')
self.message_user(request, f'{updated} вакансий закрыты.')
mark_as_closed.short_description = "Закрыть"

View File

@@ -0,0 +1,75 @@
# Generated by Django 5.1.1 on 2025-11-25 06:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0012_contactinfo'),
]
operations = [
migrations.CreateModel(
name='Career',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Название вакансии')),
('department', models.CharField(max_length=100, verbose_name='Отдел')),
('location', models.CharField(default='Кванджу, Южная Корея', max_length=200, verbose_name='Местоположение')),
('employment_type', models.CharField(choices=[('full_time', 'Полная занятость'), ('part_time', 'Частичная занятость'), ('contract', 'Контракт'), ('internship', 'Стажировка'), ('remote', 'Удаленная работа'), ('freelance', 'Фриланс')], default='full_time', max_length=20, verbose_name='Тип занятости')),
('experience_level', models.CharField(choices=[('junior', 'Junior (0-1 год)'), ('middle', 'Middle (2-4 года)'), ('senior', 'Senior (5+ лет)'), ('lead', 'Team Lead'), ('intern', 'Стажер')], default='middle', max_length=20, verbose_name='Уровень опыта')),
('description', models.TextField(verbose_name='Описание вакансии')),
('responsibilities', models.TextField(verbose_name='Обязанности')),
('requirements', models.TextField(verbose_name='Требования')),
('benefits', models.TextField(blank=True, verbose_name='Преимущества и условия')),
('salary_min', models.PositiveIntegerField(blank=True, null=True, verbose_name='Зарплата от (₩)')),
('salary_max', models.PositiveIntegerField(blank=True, null=True, verbose_name='Зарплата до (₩)')),
('salary_currency', models.CharField(default='KRW', max_length=10, verbose_name='Валюта')),
('required_skills', models.TextField(help_text='Разделите навыки запятыми', verbose_name='Обязательные навыки')),
('preferred_skills', models.TextField(blank=True, help_text='Разделите навыки запятыми', verbose_name='Желательные навыки')),
('status', models.CharField(choices=[('active', 'Активная'), ('paused', 'Приостановлена'), ('closed', 'Закрыта'), ('draft', 'Черновик')], default='active', max_length=20, verbose_name='Статус')),
('is_featured', models.BooleanField(default=False, verbose_name='Рекомендуемая вакансия')),
('application_deadline', models.DateField(blank=True, null=True, verbose_name='Дедлайн подачи заявок')),
('contact_email', models.EmailField(default='hr@smartsoltech.kr', max_length=254, verbose_name='Email для связи')),
('contact_person', models.CharField(blank=True, max_length=200, verbose_name='Контактное лицо')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('published_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата публикации')),
],
options={
'verbose_name': 'Вакансия',
'verbose_name_plural': 'Карьера',
'ordering': ['-is_featured', '-published_at', '-created_at'],
},
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='Имя')),
('last_name', models.CharField(max_length=100, verbose_name='Фамилия')),
('position', models.CharField(max_length=200, verbose_name='Должность')),
('department', models.CharField(blank=True, max_length=100, verbose_name='Отдел')),
('bio', models.TextField(blank=True, verbose_name='Биография/Описание')),
('photo', models.ImageField(blank=True, null=True, upload_to='static/img/team/', verbose_name='Фотография')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='Email')),
('phone', models.CharField(blank=True, max_length=20, verbose_name='Телефон')),
('linkedin', models.URLField(blank=True, verbose_name='LinkedIn')),
('github', models.URLField(blank=True, verbose_name='GitHub')),
('telegram', models.CharField(blank=True, max_length=100, verbose_name='Telegram')),
('skills', models.TextField(blank=True, help_text='Разделите навыки запятыми', verbose_name='Навыки')),
('experience_years', models.PositiveIntegerField(default=0, verbose_name='Лет опыта')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('display_order', models.PositiveIntegerField(default=0, verbose_name='Порядок отображения')),
('show_on_about', models.BooleanField(default=True, verbose_name='Показывать на странице О нас')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Сотрудник',
'verbose_name_plural': 'Команда',
'ordering': ['display_order', 'last_name', 'first_name'],
},
),
]

View File

@@ -212,3 +212,174 @@ class Review(models.Model):
def __str__(self):
return f"Отзыв от {self.client.first_name} {self.client.last_name} for {self.service.name}"
class Team(models.Model):
"""Модель для управления персоналом компании"""
first_name = models.CharField(max_length=100, verbose_name="Имя")
last_name = models.CharField(max_length=100, verbose_name="Фамилия")
position = models.CharField(max_length=200, verbose_name="Должность")
department = models.CharField(max_length=100, verbose_name="Отдел", blank=True)
bio = models.TextField(verbose_name="Биография/Описание", blank=True)
photo = models.ImageField(upload_to='static/img/team/', blank=True, null=True, verbose_name="Фотография")
email = models.EmailField(blank=True, verbose_name="Email")
phone = models.CharField(max_length=20, blank=True, verbose_name="Телефон")
# Социальные сети
linkedin = models.URLField(blank=True, verbose_name="LinkedIn")
github = models.URLField(blank=True, verbose_name="GitHub")
telegram = models.CharField(max_length=100, blank=True, verbose_name="Telegram")
# Навыки и технологии
skills = models.TextField(blank=True, verbose_name="Навыки", help_text="Разделите навыки запятыми")
experience_years = models.PositiveIntegerField(default=0, verbose_name="Лет опыта")
# Настройки отображения
is_active = models.BooleanField(default=True, verbose_name="Активен")
display_order = models.PositiveIntegerField(default=0, verbose_name="Порядок отображения")
show_on_about = models.BooleanField(default=True, verbose_name="Показывать на странице О нас")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Сотрудник'
verbose_name_plural = 'Команда'
ordering = ['display_order', 'last_name', 'first_name']
def __str__(self):
return f"{self.first_name} {self.last_name} - {self.position}"
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def skills_list(self):
"""Возвращает список навыков"""
if self.skills:
return [skill.strip() for skill in self.skills.split(',') if skill.strip()]
return []
class Career(models.Model):
"""Модель для управления вакансиями и карьерными возможностями"""
EMPLOYMENT_TYPE_CHOICES = [
('full_time', 'Полная занятость'),
('part_time', 'Частичная занятость'),
('contract', 'Контракт'),
('internship', 'Стажировка'),
('remote', 'Удаленная работа'),
('freelance', 'Фриланс'),
]
EXPERIENCE_LEVEL_CHOICES = [
('junior', 'Junior (0-1 год)'),
('middle', 'Middle (2-4 года)'),
('senior', 'Senior (5+ лет)'),
('lead', 'Team Lead'),
('intern', 'Стажер'),
]
STATUS_CHOICES = [
('active', 'Активная'),
('paused', 'Приостановлена'),
('closed', 'Закрыта'),
('draft', 'Черновик'),
]
title = models.CharField(max_length=200, verbose_name="Название вакансии")
department = models.CharField(max_length=100, verbose_name="Отдел")
location = models.CharField(max_length=200, default="Кванджу, Южная Корея", verbose_name="Местоположение")
employment_type = models.CharField(
max_length=20,
choices=EMPLOYMENT_TYPE_CHOICES,
default='full_time',
verbose_name="Тип занятости"
)
experience_level = models.CharField(
max_length=20,
choices=EXPERIENCE_LEVEL_CHOICES,
default='middle',
verbose_name="Уровень опыта"
)
# Описание вакансии
description = models.TextField(verbose_name="Описание вакансии")
responsibilities = models.TextField(verbose_name="Обязанности")
requirements = models.TextField(verbose_name="Требования")
benefits = models.TextField(blank=True, verbose_name="Преимущества и условия")
# Зарплатная вилка
salary_min = models.PositiveIntegerField(blank=True, null=True, verbose_name="Зарплата от (₩)")
salary_max = models.PositiveIntegerField(blank=True, null=True, verbose_name="Зарплата до (₩)")
salary_currency = models.CharField(max_length=10, default="KRW", verbose_name="Валюта")
# Необходимые навыки
required_skills = models.TextField(verbose_name="Обязательные навыки", help_text="Разделите навыки запятыми")
preferred_skills = models.TextField(blank=True, verbose_name="Желательные навыки", help_text="Разделите навыки запятыми")
# Статус и метаданные
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='active',
verbose_name="Статус"
)
is_featured = models.BooleanField(default=False, verbose_name="Рекомендуемая вакансия")
application_deadline = models.DateField(blank=True, null=True, verbose_name="Дедлайн подачи заявок")
# Контактная информация
contact_email = models.EmailField(default="hr@smartsoltech.kr", verbose_name="Email для связи")
contact_person = models.CharField(max_length=200, blank=True, verbose_name="Контактное лицо")
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(blank=True, null=True, verbose_name="Дата публикации")
class Meta:
verbose_name = 'Вакансия'
verbose_name_plural = 'Карьера'
ordering = ['-is_featured', '-published_at', '-created_at']
def __str__(self):
return f"{self.title} ({self.get_experience_level_display()})"
@property
def required_skills_list(self):
"""Возвращает список обязательных навыков"""
if self.required_skills:
return [skill.strip() for skill in self.required_skills.split(',') if skill.strip()]
return []
@property
def preferred_skills_list(self):
"""Возвращает список желательных навыков"""
if self.preferred_skills:
return [skill.strip() for skill in self.preferred_skills.split(',') if skill.strip()]
return []
@property
def salary_range(self):
"""Возвращает строку с зарплатной вилкой"""
if self.salary_min and self.salary_max:
return f"{self.salary_min:,} - ₩{self.salary_max:,}"
elif self.salary_min:
return f"от ₩{self.salary_min:,}"
elif self.salary_max:
return f"до ₩{self.salary_max:,}"
return "По договоренности"
def is_active_position(self):
"""Проверяет, активна ли вакансия"""
from django.utils import timezone
if self.status != 'active':
return False
if self.application_deadline and self.application_deadline < timezone.now().date():
return False
return True

View File

@@ -0,0 +1,637 @@
{% extends 'web/base_modern.html' %}
{% load static %}
{% block title %}Карьера в SmartSolTech - Присоединяйтесь к нашей команде{% endblock %}
{% block content %}
<!-- Hero Section -->
<section class="hero-simple bg-gradient text-white">
<div class="container-modern text-center py-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<h1 class="display-4 fw-bold mb-4">Карьера в SmartSolTech</h1>
<p class="lead opacity-90">
Развивайтесь вместе с нами в мире инновационных технологий
</p>
{% if total_positions > 0 %}
<div class="career-stats mt-4">
<div class="row justify-content-center">
<div class="col-auto">
<div class="stat-item">
<div class="stat-number">{{ total_positions }}</div>
<div class="stat-label">Открытых позиций</div>
</div>
</div>
<div class="col-auto">
<div class="stat-item">
<div class="stat-number">{{ departments|length }}</div>
<div class="stat-label">Отделов</div>
</div>
</div>
<div class="col-auto">
<div class="stat-item">
<div class="stat-number">{{ featured_careers|length }}</div>
<div class="stat-label">Топ вакансий</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</section>
<!-- Featured Positions -->
{% if featured_careers %}
<section class="section-padding">
<div class="container-modern">
<div class="text-center mb-5">
<h2 class="display-6 fw-bold text-primary mb-3">Рекомендуемые вакансии</h2>
<p class="lead text-muted">Наиболее актуальные позиции в нашей команде</p>
</div>
<div class="row g-4">
{% for career in featured_careers %}
<div class="col-lg-4 col-md-6">
<div class="career-card-featured">
<div class="career-header">
<div class="career-badges">
<span class="badge bg-warning text-dark">
<i class="fas fa-star me-1"></i>
Рекомендуем
</span>
<span class="badge bg-success">{{ career.get_employment_type_display }}</span>
</div>
<h3 class="career-title">{{ career.title }}</h3>
<p class="career-department">
<i class="fas fa-building me-2"></i>
{{ career.department }}
</p>
</div>
<div class="career-details">
<div class="detail-row">
<div class="detail-item">
<i class="fas fa-map-marker-alt text-primary"></i>
<span>{{ career.location }}</span>
</div>
<div class="detail-item">
<i class="fas fa-chart-line text-success"></i>
<span>{{ career.get_experience_level_display }}</span>
</div>
</div>
<div class="detail-row">
<div class="detail-item salary">
<i class="fas fa-won-sign text-warning"></i>
<span>{{ career.salary_range }}</span>
</div>
</div>
</div>
<div class="career-description">
<p class="text-muted">{{ career.description|truncatewords:20 }}</p>
</div>
<div class="career-requirements">
<h6 class="text-muted small mb-2">Ключевые требования:</h6>
<p class="text-muted small">{{ career.requirements|truncatewords:15 }}</p>
</div>
{% if career.required_skills_list %}
<div class="career-skills">
{% for skill in career.required_skills_list|slice:":5" %}
<span class="skill-tag">{{ skill }}</span>
{% endfor %}
{% if career.required_skills_list|length > 5 %}
<span class="skill-tag more">+{{ career.required_skills_list|length|add:"-5" }}</span>
{% endif %}
</div>
{% endif %}
<div class="career-footer">
{% if career.application_deadline %}
<div class="deadline-info mb-3">
<i class="fas fa-calendar-alt text-warning me-2"></i>
<small class="text-muted">Подача заявок до {{ career.application_deadline }}</small>
</div>
{% endif %}
<div class="d-grid gap-2">
<a href="mailto:{{ career.contact_email }}?subject=Заявка на вакансию: {{ career.title }}&body=Здравствуйте!%0A%0AМеня заинтересовала вакансия {{ career.title }} в отделе {{ career.department }}.%0A%0AОпыт работы: [укажите ваш опыт]%0AКлючевые навыки: [перечислите ваши навыки]%0A%0AПрикладываю резюме в письме.%0A%0AС уважением,[Ваше имя]"
class="btn btn-primary">
<i class="fas fa-paper-plane me-2"></i>
Откликнуться
</a>
<button class="btn btn-outline-secondary btn-sm" onclick="shareJob('{{ career.title }}', '{{ career.department }}')">
<i class="fas fa-share me-2"></i>
Поделиться
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endif %}
<!-- All Positions -->
{% if active_careers %}
<section class="section-padding {% if not featured_careers %}pt-0{% endif %} bg-light">
<div class="container-modern">
<div class="text-center mb-5">
<h2 class="display-6 fw-bold text-primary mb-3">{% if featured_careers %}Все вакансии{% else %}Открытые вакансии{% endif %}</h2>
<p class="lead text-muted">Найдите идеальную позицию для своего развития</p>
</div>
<!-- Department Filter -->
{% if departments %}
<div class="filter-section mb-5">
<div class="text-center">
<h6 class="text-muted mb-3">Фильтр по отделам:</h6>
<div class="department-filters">
<button class="filter-btn active" data-department="all">Все отделы</button>
{% for dept in departments %}
<button class="filter-btn" data-department="{{ dept }}">{{ dept }}</button>
{% endfor %}
</div>
</div>
</div>
{% endif %}
<div class="row g-4" id="careers-grid">
{% for career in active_careers %}
<div class="col-lg-6" data-department="{{ career.department }}">
<div class="career-card-compact">
<div class="card-header">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<h4 class="career-title">{{ career.title }}</h4>
<p class="career-department text-muted mb-2">{{ career.department }}</p>
<div class="career-meta">
<span class="meta-item">
<i class="fas fa-map-marker-alt me-1"></i>
{{ career.location }}
</span>
<span class="meta-item">
<i class="fas fa-briefcase me-1"></i>
{{ career.get_employment_type_display }}
</span>
<span class="meta-item">
<i class="fas fa-chart-line me-1"></i>
{{ career.get_experience_level_display }}
</span>
</div>
</div>
<div class="position-badges">
{% if career.is_featured %}
<span class="badge bg-warning text-dark small">
<i class="fas fa-star"></i>
</span>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
<p class="career-description text-muted">{{ career.description|truncatewords:25 }}</p>
{% if career.required_skills_list %}
<div class="skills-preview mb-3">
{% for skill in career.required_skills_list|slice:":4" %}
<span class="skill-tag small">{{ skill }}</span>
{% endfor %}
{% if career.required_skills_list|length > 4 %}
<span class="skill-tag small more">+{{ career.required_skills_list|length|add:"-4" }}</span>
{% endif %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div class="salary-info">
<strong class="text-success">{{ career.salary_range }}</strong>
</div>
<a href="mailto:{{ career.contact_email }}?subject=Заявка на вакансию: {{ career.title }}"
class="btn btn-primary btn-sm">
Откликнуться
</a>
</div>
</div>
{% if career.application_deadline %}
<div class="card-footer">
<small class="text-muted">
<i class="fas fa-calendar-alt me-1"></i>
Подача заявок до {{ career.application_deadline }}
</small>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% else %}
<section class="section-padding text-center">
<div class="container-modern">
<div class="py-5">
<div class="mb-4">
<i class="fas fa-briefcase text-muted" style="font-size: 4rem;"></i>
</div>
<h3 class="text-muted">В данный момент открытых вакансий нет</h3>
<p class="text-muted mb-4">Но мы всегда рады талантливым кандидатам! Отправьте нам своё резюме.</p>
<a href="mailto:hr@smartsoltech.kr" class="btn btn-primary btn-lg">
<i class="fas fa-envelope me-2"></i>
Связаться с HR
</a>
</div>
</div>
</section>
{% endif %}
<!-- Join Team CTA -->
<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">Почему выбирают SmartSolTech?</h2>
<div class="row g-4 mt-4">
<div class="col-md-3">
<div class="benefit-item">
<i class="fas fa-rocket mb-3"></i>
<h5>Инновации</h5>
<p class="small opacity-90">Работа с новейшими технологиями</p>
</div>
</div>
<div class="col-md-3">
<div class="benefit-item">
<i class="fas fa-users mb-3"></i>
<h5>Команда</h5>
<p class="small opacity-90">Дружный коллектив профессионалов</p>
</div>
</div>
<div class="col-md-3">
<div class="benefit-item">
<i class="fas fa-chart-line mb-3"></i>
<h5>Развитие</h5>
<p class="small opacity-90">Постоянное обучение и рост</p>
</div>
</div>
<div class="col-md-3">
<div class="benefit-item">
<i class="fas fa-balance-scale mb-3"></i>
<h5>Баланс</h5>
<p class="small opacity-90">Гибкий график и удаленка</p>
</div>
</div>
</div>
<div class="mt-5">
<a href="{% url 'team' %}" class="btn btn-light btn-lg me-3">
<i class="fas fa-users me-2"></i>
Наша команда
</a>
<a href="{% url 'about' %}" class="btn btn-outline-light btn-lg">
<i class="fas fa-info-circle me-2"></i>
О компании
</a>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_styles %}
<style>
/* Career Styles */
.hero-simple {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 350px;
display: flex;
align-items: center;
}
.career-stats {
margin-top: 2rem;
}
.stat-item {
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 20px;
margin: 0 15px;
}
.stat-number {
font-size: 2.5rem;
font-weight: bold;
color: white;
}
.stat-label {
font-size: 0.9rem;
opacity: 0.9;
}
.career-card-featured {
background: white;
border-radius: 20px;
padding: 30px;
transition: all 0.4s ease;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
height: 100%;
border: 2px solid transparent;
display: flex;
flex-direction: column;
}
.career-card-featured:hover {
transform: translateY(-10px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
border-color: #ffd700;
}
.career-card-compact {
background: white;
border-radius: 15px;
transition: all 0.3s ease;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
height: 100%;
overflow: hidden;
}
.career-card-compact:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.12);
}
.card-header {
padding: 25px 25px 0;
}
.card-body {
padding: 20px 25px;
}
.card-footer {
padding: 15px 25px;
background: #f8f9fa;
border-top: 1px solid #e9ecef;
}
.career-badges {
margin-bottom: 15px;
}
.career-badges .badge {
margin-right: 8px;
font-size: 0.75rem;
padding: 6px 12px;
border-radius: 15px;
}
.career-title {
font-weight: 600;
margin-bottom: 8px;
color: #2d3748;
line-height: 1.3;
}
.career-department {
margin-bottom: 15px;
font-weight: 500;
color: #6b7280;
}
.career-meta {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.meta-item {
font-size: 0.85rem;
color: #6b7280;
display: flex;
align-items: center;
}
.detail-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.detail-item {
display: flex;
align-items: center;
font-size: 0.9rem;
color: #6b7280;
}
.detail-item i {
width: 20px;
margin-right: 8px;
}
.detail-item.salary {
font-weight: 600;
color: #059669;
}
.career-description {
margin: 20px 0;
flex-grow: 1;
}
.career-requirements {
margin: 20px 0;
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
border-left: 3px solid #667eea;
}
.career-skills,
.skills-preview {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 20px;
}
.skill-tag {
background: #f1f5f9;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 4px 10px;
font-size: 0.75rem;
font-weight: 500;
color: #475569;
transition: all 0.2s ease;
}
.skill-tag:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.skill-tag.more {
background: #667eea;
color: white;
border-color: #667eea;
}
.skill-tag.small {
font-size: 0.7rem;
padding: 3px 8px;
}
.career-footer {
margin-top: auto;
}
.deadline-info {
padding: 10px;
background: #fef3c7;
border-radius: 8px;
}
.department-filters {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.filter-btn {
background: white;
border: 2px solid #e2e8f0;
border-radius: 25px;
padding: 8px 20px;
font-size: 0.9rem;
font-weight: 500;
color: #6b7280;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-btn:hover,
.filter-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.position-badges {
display: flex;
gap: 5px;
}
.salary-info {
font-size: 1rem;
}
.benefit-item {
padding: 20px;
}
.benefit-item i {
font-size: 2.5rem;
margin-bottom: 1rem;
color: rgba(255, 255, 255, 0.9);
}
.benefit-item h5 {
color: white;
margin-bottom: 0.5rem;
}
/* Responsive */
@media (max-width: 768px) {
.career-stats .row {
justify-content: center;
}
.stat-item {
margin: 10px 5px;
padding: 15px;
}
.stat-number {
font-size: 2rem;
}
.career-meta {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.detail-row {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.department-filters {
justify-content: flex-start;
}
.filter-btn {
font-size: 0.8rem;
padding: 6px 15px;
}
}
</style>
{% endblock %}
{% block extra_scripts %}
<script>
// Department filter functionality
document.addEventListener('DOMContentLoaded', function() {
const filterBtns = document.querySelectorAll('.filter-btn');
const careerCards = document.querySelectorAll('#careers-grid > div');
filterBtns.forEach(btn => {
btn.addEventListener('click', function() {
// Update active button
filterBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
const department = this.dataset.department;
careerCards.forEach(card => {
if (department === 'all' || card.dataset.department === department) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
});
});
});
// Share job functionality
function shareJob(title, department) {
if (navigator.share) {
navigator.share({
title: `Вакансия: ${title}`,
text: `Открыта вакансия "${title}" в отделе ${department} в SmartSolTech`,
url: window.location.href
});
} else {
// Fallback - copy to clipboard
const text = `Вакансия: ${title} в ${department} - SmartSolTech\n${window.location.href}`;
navigator.clipboard.writeText(text).then(() => {
alert('Ссылка скопирована в буфер обмена!');
});
}
}
</script>
{% endblock %}

View File

@@ -442,6 +442,180 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
</div>
</section>
<!-- Team Section -->
{% if team_members %}
<section class="section-padding bg-light">
<div class="container-modern">
<div class="row justify-content-center mb-5">
<div class="col-lg-8 text-center">
<h2 class="display-5 fw-bold text-primary mb-4">Наша команда</h2>
<p class="lead text-muted">
Профессионалы, которые воплощают ваши идеи в реальность
</p>
</div>
</div>
<div class="row g-4">
{% for member in team_members %}
<div class="col-lg-3 col-md-6">
<div class="team-card">
<div class="team-image-wrapper">
{% if member.photo %}
<img src="{{ member.photo.url }}" alt="{{ member.full_name }}" class="team-image">
{% else %}
<div class="team-placeholder">
<i class="fas fa-user"></i>
</div>
{% endif %}
<div class="team-overlay">
<div class="team-social">
{% if member.linkedin %}
<a href="{{ member.linkedin }}" target="_blank" class="social-link">
<i class="fab fa-linkedin"></i>
</a>
{% endif %}
{% if member.github %}
<a href="{{ member.github }}" target="_blank" class="social-link">
<i class="fab fa-github"></i>
</a>
{% endif %}
{% if member.telegram %}
<a href="https://t.me/{{ member.telegram|cut:'@' }}" target="_blank" class="social-link">
<i class="fab fa-telegram"></i>
</a>
{% endif %}
</div>
</div>
</div>
<div class="team-info">
<h5 class="team-name">{{ member.full_name }}</h5>
<p class="team-position text-primary">{{ member.position }}</p>
{% if member.department %}
<p class="team-department text-muted small">{{ member.department }}</p>
{% endif %}
{% if member.experience_years > 0 %}
<div class="team-experience">
<small class="text-muted">
<i class="fas fa-clock me-1"></i>
{{ member.experience_years }}+ лет опыта
</small>
</div>
{% endif %}
{% if member.skills_list %}
<div class="team-skills mt-2">
{% for skill in member.skills_list|slice:":3" %}
<span class="skill-tag">{{ skill }}</span>
{% endfor %}
{% if member.skills_list|length > 3 %}
<span class="skill-tag more">+{{ member.skills_list|length|add:"-3" }}</span>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="text-center mt-5">
<a href="{% url 'about' %}" class="btn btn-primary btn-lg">
<i class="fas fa-users me-2"></i>
Познакомиться с командой
</a>
</div>
</div>
</section>
{% endif %}
<!-- Career Section -->
{% if featured_careers %}
<section class="section-padding">
<div class="container-modern">
<div class="row justify-content-center mb-5">
<div class="col-lg-8 text-center">
<h2 class="display-5 fw-bold text-primary mb-4">Карьера в SmartSolTech</h2>
<p class="lead text-muted">
Присоединяйтесь к нашей команде и развивайтесь в мире технологий
</p>
</div>
</div>
<div class="row g-4">
{% for career in featured_careers %}
<div class="col-lg-4 col-md-6">
<div class="career-card">
<div class="career-header">
<div class="career-badge">
<span class="badge bg-success">{{ career.get_employment_type_display }}</span>
{% if career.is_featured %}
<span class="badge bg-warning text-dark">Рекомендуем</span>
{% endif %}
</div>
<h4 class="career-title">{{ career.title }}</h4>
<p class="career-department text-muted">
<i class="fas fa-building me-1"></i>
{{ career.department }}
</p>
</div>
<div class="career-details">
<div class="detail-item">
<i class="fas fa-map-marker-alt text-primary"></i>
<span>{{ career.location }}</span>
</div>
<div class="detail-item">
<i class="fas fa-chart-line text-success"></i>
<span>{{ career.get_experience_level_display }}</span>
</div>
<div class="detail-item">
<i class="fas fa-won-sign text-warning"></i>
<span>{{ career.salary_range }}</span>
</div>
</div>
<div class="career-description">
<p class="text-muted">{{ career.description|truncatewords:15 }}</p>
</div>
{% if career.required_skills_list %}
<div class="career-skills">
{% for skill in career.required_skills_list|slice:":4" %}
<span class="skill-tag small">{{ skill }}</span>
{% endfor %}
{% if career.required_skills_list|length > 4 %}
<span class="skill-tag small more">+{{ career.required_skills_list|length|add:"-4" }}</span>
{% endif %}
</div>
{% endif %}
<div class="career-footer">
{% if career.application_deadline %}
<small class="text-muted d-block mb-2">
<i class="fas fa-calendar-alt me-1"></i>
До {{ career.application_deadline }}
</small>
{% endif %}
<a href="mailto:{{ career.contact_email }}?subject=Заявка на вакансию: {{ career.title }}"
class="btn btn-primary w-100">
<i class="fas fa-paper-plane me-2"></i>
Откликнуться
</a>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="text-center mt-5">
<a href="{% url 'about' %}#career" class="btn btn-outline-primary btn-lg">
<i class="fas fa-briefcase me-2"></i>
Все вакансии
</a>
</div>
</div>
</section>
{% endif %}
{% endblock %}
{% block extra_styles %}
@@ -676,5 +850,254 @@ document.addEventListener('DOMContentLoaded', function() {
padding: 8px 16px;
}
}
/* Team Section Styles */
.team-card {
background: white;
border-radius: 20px;
overflow: hidden;
transition: all 0.4s ease;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
height: 100%;
}
.team-card:hover {
transform: translateY(-10px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}
.team-image-wrapper {
position: relative;
width: 100%;
height: 250px;
overflow: hidden;
}
.team-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.team-placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 4rem;
}
.team-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
opacity: 0;
transition: opacity 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.team-card:hover .team-overlay {
opacity: 1;
}
.team-card:hover .team-image {
transform: scale(1.1);
}
.team-social {
display: flex;
gap: 15px;
}
.social-link {
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #667eea;
text-decoration: none;
font-size: 1.5rem;
transition: all 0.3s ease;
}
.social-link:hover {
transform: scale(1.1) rotate(5deg);
color: #764ba2;
}
.team-info {
padding: 25px 20px;
}
.team-name {
font-weight: 600;
margin-bottom: 8px;
color: #2d3748;
}
.team-position {
font-weight: 500;
margin-bottom: 8px;
}
.team-department {
margin-bottom: 12px;
}
.team-experience {
margin-bottom: 15px;
}
.team-skills {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.skill-tag {
background: #f7fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 4px 10px;
font-size: 0.75rem;
font-weight: 500;
color: #4a5568;
transition: all 0.2s ease;
}
.skill-tag:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.skill-tag.more {
background: #667eea;
color: white;
border-color: #667eea;
}
/* Career Section Styles */
.career-card {
background: white;
border-radius: 20px;
padding: 30px;
transition: all 0.4s ease;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
height: 100%;
border: 2px solid transparent;
display: flex;
flex-direction: column;
}
.career-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
border-color: #667eea;
}
.career-header {
margin-bottom: 20px;
}
.career-badge {
margin-bottom: 15px;
}
.career-badge .badge {
margin-right: 8px;
font-size: 0.75rem;
padding: 6px 12px;
border-radius: 20px;
}
.career-title {
font-weight: 600;
margin-bottom: 8px;
color: #2d3748;
line-height: 1.3;
}
.career-department {
margin-bottom: 0;
font-weight: 500;
}
.career-details {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #e2e8f0;
}
.detail-item {
display: flex;
align-items: center;
margin-bottom: 10px;
font-size: 0.9rem;
}
.detail-item i {
width: 20px;
margin-right: 10px;
flex-shrink: 0;
}
.career-description {
margin-bottom: 20px;
flex-grow: 1;
}
.career-skills {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 25px;
}
.career-skills .skill-tag.small {
font-size: 0.7rem;
padding: 3px 8px;
}
.career-footer {
margin-top: auto;
}
/* Responsive for Team and Career */
@media (max-width: 768px) {
.team-image-wrapper {
height: 200px;
}
.team-info {
padding: 20px 15px;
}
.social-link {
width: 40px;
height: 40px;
font-size: 1.2rem;
}
.career-card {
padding: 25px 20px;
}
.career-title {
font-size: 1.1rem;
}
}
</style>
{% endblock %}

View File

@@ -31,6 +31,18 @@
<i class="fas fa-info-circle me-2"></i>О нас
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'team' %}active{% endif %}"
href="{% url 'team' %}">
<i class="fas fa-users me-2"></i>Команда
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'career' %}active{% endif %}"
href="{% url 'career' %}">
<i class="fas fa-briefcase me-2"></i>Карьера
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern" href="#contact">
<i class="fas fa-envelope me-2"></i>Контакты

View File

@@ -31,6 +31,18 @@
<i class="fas fa-info-circle me-2"></i>О нас
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'team' %}active{% endif %}"
href="{% url 'team' %}">
<i class="fas fa-users me-2"></i>Команда
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern {% if request.resolver_match.url_name == 'career' %}active{% endif %}"
href="{% url 'career' %}">
<i class="fas fa-briefcase me-2"></i>Карьера
</a>
</li>
<li class="nav-item">
<a class="nav-link-modern" href="#contact">
<i class="fas fa-envelope me-2"></i>Контакты

View File

@@ -0,0 +1,316 @@
{% extends 'web/base_modern.html' %}
{% load static %}
{% block title %}Наша команда - SmartSolTech{% endblock %}
{% block content %}
<!-- Hero Section -->
<section class="hero-simple bg-gradient text-white">
<div class="container-modern text-center py-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<h1 class="display-4 fw-bold mb-4">Познакомьтесь с нашей командой</h1>
<p class="lead opacity-90">
Профессионалы, которые превращают идеи в инновационные технологические решения
</p>
</div>
</div>
</div>
</section>
<!-- Team Grid Section -->
<section class="section-padding">
<div class="container-modern">
{% if team_members %}
<div class="row g-4">
{% for member in team_members %}
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="team-card-detailed">
<div class="team-image-wrapper">
{% if member.photo %}
<img src="{{ member.photo.url }}" alt="{{ member.full_name }}" class="team-image">
{% else %}
<div class="team-placeholder">
<i class="fas fa-user"></i>
</div>
{% endif %}
<div class="team-overlay">
<div class="team-social">
{% if member.email %}
<a href="mailto:{{ member.email }}" class="social-link" title="Email">
<i class="fas fa-envelope"></i>
</a>
{% endif %}
{% if member.linkedin %}
<a href="{{ member.linkedin }}" target="_blank" class="social-link" title="LinkedIn">
<i class="fab fa-linkedin"></i>
</a>
{% endif %}
{% if member.github %}
<a href="{{ member.github }}" target="_blank" class="social-link" title="GitHub">
<i class="fab fa-github"></i>
</a>
{% endif %}
{% if member.telegram %}
<a href="https://t.me/{{ member.telegram|cut:'@' }}" target="_blank" class="social-link" title="Telegram">
<i class="fab fa-telegram"></i>
</a>
{% endif %}
{% if member.phone %}
<a href="tel:{{ member.phone }}" class="social-link" title="Телефон">
<i class="fas fa-phone"></i>
</a>
{% endif %}
</div>
</div>
</div>
<div class="team-info-detailed">
<h4 class="team-name">{{ member.full_name }}</h4>
<p class="team-position text-primary">{{ member.position }}</p>
{% if member.department %}
<div class="team-department">
<i class="fas fa-building me-2 text-muted"></i>
<span class="text-muted">{{ member.department }}</span>
</div>
{% endif %}
{% if member.experience_years > 0 %}
<div class="team-experience">
<i class="fas fa-clock me-2 text-muted"></i>
<span class="text-muted">{{ member.experience_years }}+ лет опыта</span>
</div>
{% endif %}
{% if member.bio %}
<div class="team-bio mt-3">
<p class="text-muted small">{{ member.bio|truncatewords:20 }}</p>
</div>
{% endif %}
{% if member.skills_list %}
<div class="team-skills mt-3">
<h6 class="text-muted small mb-2">Навыки:</h6>
<div class="skills-wrap">
{% for skill in member.skills_list %}
<span class="skill-tag">{{ skill }}</span>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<div class="mb-4">
<i class="fas fa-users text-muted" style="font-size: 4rem;"></i>
</div>
<h3 class="text-muted">Команда пока не добавлена</h3>
<p class="text-muted">Мы работаем над обновлением информации о нашей команде.</p>
</div>
{% endif %}
</div>
</section>
<!-- Join Team CTA -->
<section class="section-padding bg-light">
<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 text-muted mb-5">
Мы всегда ищем талантливых и мотивированных профессионалов
</p>
<div class="d-flex flex-wrap gap-3 justify-content-center">
<a href="{% url 'career' %}" class="btn btn-primary btn-lg">
<i class="fas fa-briefcase me-2"></i>
Открытые вакансии
</a>
<a href="mailto:hr@smartsoltech.kr" class="btn btn-outline-primary btn-lg">
<i class="fas fa-envelope me-2"></i>
Связаться с HR
</a>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_styles %}
<style>
/* Team Detailed Styles */
.hero-simple {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 300px;
display: flex;
align-items: center;
}
.team-card-detailed {
background: white;
border-radius: 24px;
overflow: hidden;
transition: all 0.4s ease;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
height: 100%;
}
.team-card-detailed:hover {
transform: translateY(-15px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2);
}
.team-image-wrapper {
position: relative;
width: 100%;
height: 280px;
overflow: hidden;
}
.team-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.team-placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 4rem;
}
.team-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
opacity: 0;
transition: opacity 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.team-card-detailed:hover .team-overlay {
opacity: 1;
}
.team-card-detailed:hover .team-image {
transform: scale(1.1);
}
.team-social {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.social-link {
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #667eea;
text-decoration: none;
font-size: 1.3rem;
transition: all 0.3s ease;
}
.social-link:hover {
transform: scale(1.1) rotate(5deg);
color: #764ba2;
}
.team-info-detailed {
padding: 30px 25px;
}
.team-name {
font-weight: 600;
margin-bottom: 8px;
color: #2d3748;
font-size: 1.25rem;
}
.team-position {
font-weight: 500;
margin-bottom: 15px;
font-size: 1rem;
}
.team-department,
.team-experience {
display: flex;
align-items: center;
margin-bottom: 12px;
font-size: 0.9rem;
}
.team-bio {
border-left: 3px solid #e2e8f0;
padding-left: 15px;
}
.skills-wrap {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.skill-tag {
background: #f7fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 5px 12px;
font-size: 0.75rem;
font-weight: 500;
color: #4a5568;
transition: all 0.2s ease;
}
.skill-tag:hover {
background: #667eea;
color: white;
border-color: #667eea;
transform: translateY(-2px);
}
/* Responsive */
@media (max-width: 768px) {
.team-image-wrapper {
height: 220px;
}
.team-info-detailed {
padding: 25px 20px;
}
.social-link {
width: 45px;
height: 45px;
font-size: 1.1rem;
}
.team-social {
gap: 10px;
}
}
</style>
{% endblock %}

View File

@@ -15,6 +15,7 @@ urlpatterns = [
path('blog/', views.blog_view, name='blog'),
path('news/', views.news_view, name='news'),
path('career/', views.career_view, name='career'),
path('team/', views.team_view, name='team'),
# path('create_order/<int:pk>/', views.create_order, name='create_order'),
path('about/', views.about_view, name="about"),
path('service/generate_qr_code/<int:service_id>/', views.generate_qr_code, name='generate_qr_code'),

View File

@@ -1,5 +1,5 @@
from django.shortcuts import render, get_object_or_404, redirect
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, HeroBanner, Category, ContactInfo
from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, HeroBanner, Category, ContactInfo, Team, Career
from django.db.models import Avg
from comunication.models import TelegramSettings
import qrcode
@@ -41,12 +41,18 @@ def home(request):
latest_blog_posts = BlogPost.objects.all()[:2] # Последние статьи блога
reviews = Review.objects.all()[:3] # Отзывы клиентов
# Данные для команды и карьеры
team_members = Team.objects.filter(is_active=True, show_on_about=True).order_by('display_order')[:4] # Показываем топ-4 участников команды
featured_careers = Career.objects.filter(status='active', is_featured=True).order_by('-created_at')[:3] # Топ-3 рекомендуемые вакансии
return render(request, 'web/home_modern.html', {
'services': services,
'hero_banners': hero_banners,
'latest_projects': latest_projects,
'latest_blog_posts': latest_blog_posts,
'reviews': reviews,
'team_members': team_members,
'featured_careers': featured_careers,
})
def service_detail(request, pk):
@@ -359,3 +365,30 @@ def check_request_status(request, request_id):
except Exception as e:
logger.error(f"Ошибка при проверке статуса заявки {request_id}: {str(e)}")
return JsonResponse({'error': 'Ошибка сервера'}, status=500)
def team_view(request):
"""Страница команды"""
team_members = Team.objects.filter(is_active=True).order_by('display_order', 'last_name')
return render(request, 'web/team.html', {
'team_members': team_members,
})
def career_view(request):
"""Страница карьеры"""
# Активные вакансии
active_careers = Career.objects.filter(status='active').order_by('-is_featured', '-created_at')
# Рекомендуемые вакансии
featured_careers = active_careers.filter(is_featured=True)
# Статистика
departments = Career.objects.filter(status='active').values_list('department', flat=True).distinct()
return render(request, 'web/career.html', {
'active_careers': active_careers,
'featured_careers': featured_careers,
'departments': departments,
'total_positions': active_careers.count(),
})