From ce7119e9e9c881c9ca1fde7ec647ac5695a77f4b Mon Sep 17 00:00:00 2001 From: "Andrew K. Choi" Date: Mon, 24 Nov 2025 09:23:45 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20Tea?= =?UTF-8?q?mMember=20=D0=B4=D0=BB=D1=8F=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD?= =?UTF-8?q?=D0=B4=D0=BE=D0=B9=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=B0?= =?UTF-8?q?=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- smartsoltech/web/admin.py | 32 ++++++- .../commands/create_team_members.py | 65 ++++++++++++++ .../web/migrations/0011_teammember.py | 39 ++++++++ smartsoltech/web/models.py | 63 +++++++++++++ .../web/templates/web/team_section.html | 90 +++++++++++++++++++ smartsoltech/web/views.py | 8 +- 6 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 smartsoltech/web/management/commands/create_team_members.py create mode 100644 smartsoltech/web/migrations/0011_teammember.py create mode 100644 smartsoltech/web/templates/web/team_section.html diff --git a/smartsoltech/web/admin.py b/smartsoltech/web/admin.py index 8a69a3f..b42c524 100644 --- a/smartsoltech/web/admin.py +++ b/smartsoltech/web/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from .models import ( Service, Project, Client, Order, Review, BlogPost, - Category, ServiceRequest, AboutPage, FooterSettings + Category, ServiceRequest, AboutPage, FooterSettings, TeamMember ) from .forms import ProjectForm @@ -150,3 +150,33 @@ class FooterSettingsAdmin(admin.ModelAdmin): def has_add_permission(self, request): return not FooterSettings.objects.filter(is_active=True).exists() + + +@admin.register(TeamMember) +class TeamMemberAdmin(admin.ModelAdmin): + list_display = ('full_name', 'position', 'order', 'is_active', 'email', 'updated_at') + list_filter = ('is_active', 'position') + search_fields = ('first_name', 'last_name', 'position', 'email') + list_editable = ('order', 'is_active') + + fieldsets = ( + ('Основная информация', { + 'fields': ('first_name', 'last_name', 'position', 'photo') + }), + ('О специалисте', { + 'fields': ('bio', 'specialization') + }), + ('Контакты', { + 'fields': ('email', 'phone', 'telegram', 'linkedin', 'github'), + 'classes': ('collapse',) + }), + ('Настройки отображения', { + 'fields': ('order', 'is_active') + }), + ) + + def full_name(self, obj): + return obj.full_name + full_name.short_description = 'ФИО' + full_name.admin_order_field = 'last_name' + diff --git a/smartsoltech/web/management/commands/create_team_members.py b/smartsoltech/web/management/commands/create_team_members.py new file mode 100644 index 0000000..ed1eef7 --- /dev/null +++ b/smartsoltech/web/management/commands/create_team_members.py @@ -0,0 +1,65 @@ +from django.core.management.base import BaseCommand +from web.models import TeamMember + + +class Command(BaseCommand): + help = 'Создает тестовых членов команды' + + def handle(self, *args, **options): + # Проверяем, есть ли уже члены команды + if TeamMember.objects.exists(): + self.stdout.write(self.style.WARNING('⚠️ Члены команды уже существуют')) + return + + team_members = [ + { + 'first_name': 'Алексей', + 'last_name': 'Чой', + 'position': 'CEO & Founder', + 'bio': 'Визионер и лидер команды с более чем 5-летним опытом в IT-индустрии. Специализируется на стратегическом планировании и управлении проектами.', + 'specialization': 'Стратегическое планирование, управление проектами, бизнес-аналитика', + 'email': 'alexey@smartsoltech.kr', + 'telegram': 'alexey_choi', + 'linkedin': 'https://linkedin.com/in/alexey-choi', + 'github': 'https://github.com/alexeychoi', + 'order': 1, + 'is_active': True + }, + { + 'first_name': 'Анна', + 'last_name': 'Ким', + 'position': 'Lead Developer', + 'bio': 'Опытный full-stack разработчик со страстью к созданию масштабируемых и эффективных веб-приложений. Эксперт в React, Django и cloud технологиях.', + 'specialization': 'React, Django, Docker, PostgreSQL, AWS', + 'email': 'anna@smartsoltech.kr', + 'telegram': 'anna_kim_dev', + 'linkedin': 'https://linkedin.com/in/anna-kim', + 'github': 'https://github.com/annakim', + 'order': 2, + 'is_active': True + }, + { + 'first_name': 'Дмитрий', + 'last_name': 'Пак', + 'position': 'UI/UX Designer', + 'bio': 'Креативный дизайнер, создающий интуитивные и привлекательные пользовательские интерфейсы. Специализируется на UX-исследованиях и современном веб-дизайне.', + 'specialization': 'Figma, Adobe XD, Sketch, UI Design, UX Research', + 'email': 'dmitry@smartsoltech.kr', + 'telegram': 'dmitry_pak', + 'linkedin': 'https://linkedin.com/in/dmitry-pak', + 'order': 3, + 'is_active': True + }, + ] + + created_count = 0 + for member_data in team_members: + member = TeamMember.objects.create(**member_data) + created_count += 1 + self.stdout.write( + self.style.SUCCESS(f'✅ Создан: {member.full_name} - {member.position}') + ) + + self.stdout.write( + self.style.SUCCESS(f'\n✨ Готово! Создано {created_count} членов команды') + ) diff --git a/smartsoltech/web/migrations/0011_teammember.py b/smartsoltech/web/migrations/0011_teammember.py new file mode 100644 index 0000000..538901b --- /dev/null +++ b/smartsoltech/web/migrations/0011_teammember.py @@ -0,0 +1,39 @@ +# Generated by Django 5.1.1 on 2025-11-24 00:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0010_aboutpage_footersettings'), + ] + + operations = [ + migrations.CreateModel( + name='TeamMember', + 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='Должность')), + ('photo', models.ImageField(blank=True, null=True, upload_to='static/img/team/', verbose_name='Фотография')), + ('bio', models.TextField(blank=True, help_text='Краткое описание специалиста', verbose_name='Биография')), + ('specialization', models.TextField(blank=True, help_text='Области экспертизы, навыки', verbose_name='Специализация')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='Email')), + ('phone', models.CharField(blank=True, max_length=50, verbose_name='Телефон')), + ('telegram', models.CharField(blank=True, max_length=100, verbose_name='Telegram')), + ('linkedin', models.URLField(blank=True, verbose_name='LinkedIn')), + ('github', models.URLField(blank=True, verbose_name='GitHub')), + ('order', models.IntegerField(default=0, help_text='Чем меньше число, тем выше в списке', verbose_name='Порядок сортировки')), + ('is_active', models.BooleanField(default=True, verbose_name='Активен')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')), + ], + options={ + 'verbose_name': 'Член команды', + 'verbose_name_plural': 'Команда', + 'ordering': ['order', 'last_name', 'first_name'], + }, + ), + ] diff --git a/smartsoltech/web/models.py b/smartsoltech/web/models.py index 70981ee..73e80b7 100644 --- a/smartsoltech/web/models.py +++ b/smartsoltech/web/models.py @@ -341,3 +341,66 @@ class FooterSettings(models.Model): super().save(*args, **kwargs) +class TeamMember(models.Model): + """Модель для членов команды""" + + # Basic Info + 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='Должность') + + # Photo + photo = models.ImageField( + upload_to='static/img/team/', + blank=True, + null=True, + verbose_name='Фотография' + ) + + # Bio + bio = models.TextField( + blank=True, + verbose_name='Биография', + help_text='Краткое описание специалиста' + ) + + # Skills/Specialization + specialization = models.TextField( + blank=True, + verbose_name='Специализация', + help_text='Области экспертизы, навыки' + ) + + # 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') + linkedin = models.URLField(blank=True, verbose_name='LinkedIn') + github = models.URLField(blank=True, verbose_name='GitHub') + + # Order and Status + order = models.IntegerField( + default=0, + verbose_name='Порядок сортировки', + help_text='Чем меньше число, тем выше в списке' + ) + is_active = models.BooleanField(default=True, verbose_name='Активен') + + # Timestamps + created_at = models.DateTimeField(auto_now_add=True, verbose_name='Создано') + updated_at = models.DateTimeField(auto_now=True, verbose_name='Обновлено') + + class Meta: + verbose_name = 'Член команды' + verbose_name_plural = 'Команда' + ordering = ['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}" + + + diff --git a/smartsoltech/web/templates/web/team_section.html b/smartsoltech/web/templates/web/team_section.html new file mode 100644 index 0000000..acfa929 --- /dev/null +++ b/smartsoltech/web/templates/web/team_section.html @@ -0,0 +1,90 @@ + +
+
+
+ + {{ about.team_badge|default:"👥 Команда" }} + +

+ {{ about.team_title|default:"Познакомьтесь с нашей командой"|safe }} +

+

+ {{ about.team_description|default:"Талантливые профессионалы, которые воплощают ваши идеи в реальность" }} +

+
+ + {% if team_members %} +
+ {% for member in team_members %} +
+
+
+ {% if member.photo %} + {{ member.full_name }} + {% else %} +
+ + {{ member.first_name|first }}{{ member.last_name|first }} + +
+ {% endif %} +
+
+
{{ member.full_name }}
+

{{ member.position }}

+ + {% if member.bio %} +

+ {{ member.bio|truncatewords:30 }} +

+ {% endif %} + + {% if member.specialization %} +
+ + + {{ member.specialization|truncatewords:10 }} + +
+ {% endif %} + +
+ {% if member.linkedin %} + + + + {% endif %} + + {% if member.github %} + + + + {% endif %} + + {% if member.telegram %} + + + + {% endif %} + + {% if member.email %} + + + + {% endif %} +
+
+
+
+ {% endfor %} +
+ {% else %} +
+

Информация о команде скоро появится

+
+ {% endif %} +
+
diff --git a/smartsoltech/web/views.py b/smartsoltech/web/views.py index a33666d..898df72 100644 --- a/smartsoltech/web/views.py +++ b/smartsoltech/web/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render, get_object_or_404, redirect -from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, Category, AboutPage, FooterSettings +from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest, Category, AboutPage, FooterSettings, TeamMember from django.db.models import Avg from comunication.models import TelegramSettings import qrcode @@ -88,7 +88,11 @@ def services_view(request): def about_view(request): about_page = AboutPage.objects.filter(is_active=True).first() - return render(request, 'web/about_modern.html', {'about': about_page}) + team_members = TeamMember.objects.filter(is_active=True).order_by('order', 'last_name') + return render(request, 'web/about_modern.html', { + 'about': about_page, + 'team_members': team_members + }) def create_service_request(request, service_id): if request.method == 'POST':