Перемещние потомков
This commit is contained in:
parent
30e2eeb736
commit
a8785eac68
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -26,6 +26,15 @@ class Recall(models.Model):
|
|||||||
verbose_name_plural = 'Отзывы'
|
verbose_name_plural = 'Отзывы'
|
||||||
ordering = ['time_create', 'title']
|
ordering = ['time_create', 'title']
|
||||||
|
|
||||||
|
def get_seo_title(self):
|
||||||
|
return f"Отзыв от {self.title} | Программист 1С"
|
||||||
|
|
||||||
|
def get_seo_description(self):
|
||||||
|
if self.content:
|
||||||
|
clean_content = self.content[:160].replace('\n', ' ').strip()
|
||||||
|
return f"Отзыв о работе программиста 1С от {self.title}. {clean_content}..."
|
||||||
|
return f"Отзыв клиента {self.title} о работе программиста 1С Николая Сердюк"
|
||||||
|
|
||||||
|
|
||||||
class Competence(models.Model):
|
class Competence(models.Model):
|
||||||
title = models.CharField(max_length=255, verbose_name='Программист')
|
title = models.CharField(max_length=255, verbose_name='Программист')
|
||||||
@ -67,6 +76,23 @@ class Solution(models.Model):
|
|||||||
verbose_name_plural = 'Проекты'
|
verbose_name_plural = 'Проекты'
|
||||||
ordering = ['time_create', 'title']
|
ordering = ['time_create', 'title']
|
||||||
|
|
||||||
|
def get_seo_title(self):
|
||||||
|
"""Генерирует SEO-заголовок для проекта"""
|
||||||
|
return f"Проект: {self.title} | Автоматизация 1С"
|
||||||
|
|
||||||
|
def get_seo_description(self):
|
||||||
|
"""Генерирует SEO-описание для проекта"""
|
||||||
|
if self.description:
|
||||||
|
clean_desc = self.description[:160].replace('\n', ' ').strip()
|
||||||
|
return f"Проект автоматизации: {self.title}. {clean_desc}..."
|
||||||
|
return f"Реализация проекта {self.title} - программист 1С Николай Сердюк"
|
||||||
|
|
||||||
|
def get_meta_keywords(self):
|
||||||
|
"""Автоматические ключевые слова для проекта"""
|
||||||
|
base_keywords = ["проект 1С", "автоматизация 1С", "внедрение 1С"]
|
||||||
|
title_words = self.title.lower().split()
|
||||||
|
return base_keywords + title_words
|
||||||
|
|
||||||
|
|
||||||
class Home(models.Model):
|
class Home(models.Model):
|
||||||
title = models.CharField(max_length=255, verbose_name='Наименование')
|
title = models.CharField(max_length=255, verbose_name='Наименование')
|
||||||
|
|||||||
@ -4,15 +4,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<title>{{title}} - Программист 1С</title>
|
<title>{{title}}</title>
|
||||||
<!-- Основные мета-теги -->
|
<!-- Основные мета-теги -->
|
||||||
<meta name="description" content="{% block meta_description %}Профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция и оптимизация систем 1С.{% endblock %}">
|
<meta name="description" content="{% block meta_description %}{{ meta_description|default:'Профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция и оптимизация систем 1С.' }}{% endblock %}">
|
||||||
<meta name="keywords" content="{% block meta_keywords %}программист 1С, разработка 1С, интеграция 1С, оптимизация 1С, 1С предприятие{% endblock %}">
|
<meta name="keywords" content="{% block meta_keywords %}{{ meta_keywords|default:'программист 1С, разработка 1С, интеграция 1С, оптимизация 1С, 1С предприятие' }}{% endblock %}">
|
||||||
<meta name="author" content="Николай Сердюк">
|
<meta name="author" content="Николай Сердюк">
|
||||||
|
|
||||||
<!-- Open Graph для соцсетей -->
|
<!-- Open Graph для соцсетей -->
|
||||||
<meta property="og:title" content="{{title}} - Программист 1С">
|
<meta property="og:title" content="{{title}}">
|
||||||
<meta property="og:description" content="Профессиональный программист 1С с более чем 10-летним опытом">
|
<meta property="og:description" content="{% block og_description %}{{ meta_description|default:'Профессиональный программист 1С с более чем 10-летним опытом' }}{% endblock %}">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||||
<meta property="og:image" content="{% static 'programmer/images/og-image.jpg' %}">
|
<meta property="og:image" content="{% static 'programmer/images/og-image.jpg' %}">
|
||||||
@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
<!-- Twitter Card -->
|
<!-- Twitter Card -->
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:title" content="{{title}} - Программист 1С">
|
<meta name="twitter:title" content="{{title}}">
|
||||||
<meta name="twitter:description" content="Профессиональный программист 1С с более чем 10-летним опытом">
|
<meta name="twitter:description" content="{% block twitter_description %}{{ meta_description|default:'Профессиональный программист 1С с более чем 10-летним опытом' }}{% endblock %}">
|
||||||
<meta name="twitter:image" content="{% static 'programmer/images/og-image.jpg' %}">
|
<meta name="twitter:image" content="{% static 'programmer/images/og-image.jpg' %}">
|
||||||
|
|
||||||
<!-- Дополнительные SEO-теги -->
|
<!-- Дополнительные SEO-теги -->
|
||||||
@ -551,5 +551,28 @@
|
|||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
<!-- Дополнительные JS файлы для конкретных страниц -->
|
<!-- Дополнительные JS файлы для конкретных страниц -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- В recall.html после основного контента -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Review",
|
||||||
|
"itemReviewed": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Услуги программиста 1С"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "ООО «РОВЕН-Регионы»"
|
||||||
|
},
|
||||||
|
"reviewRating": {
|
||||||
|
"@type": "Rating",
|
||||||
|
"ratingValue": "5",
|
||||||
|
"bestRating": "5"
|
||||||
|
},
|
||||||
|
"datePublished": "2025-11-13",
|
||||||
|
"description": "Выражаю благодарность программисту 1С Николаю Сердюк за профессиональную работу и качественное решение поставленных задач..."
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<div class="competence-scan-wrapper">
|
<div class="competence-scan-wrapper">
|
||||||
<div class="competence-scan-container">
|
<div class="competence-scan-container">
|
||||||
<img src="{{ p.photo.url }}"
|
<img src="{{ p.photo.url }}"
|
||||||
alt="{{ p.title }}"
|
alt="Сертификат 1С: {{ p.title }} - {{ p.content|striptags }}"
|
||||||
class="competence-scan"
|
class="competence-scan"
|
||||||
onclick="openCompetenceModal('{{ p.photo.url }}', '{{ p.title }}')">
|
onclick="openCompetenceModal('{{ p.photo.url }}', '{{ p.title }}')">
|
||||||
<div class="scan-hint">
|
<div class="scan-hint">
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends 'programmer/base.html' %}
|
{% extends 'programmer/base.html' %}
|
||||||
{% load django_bootstrap5 %}
|
{% load django_bootstrap5 %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load seo_tags %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="{% static 'programmer/css/recall.css' %}">
|
<link rel="stylesheet" href="{% static 'programmer/css/recall.css' %}">
|
||||||
@ -8,13 +9,37 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1 class="page-title">Отзывы</h1>
|
<h1 class="page-title">Отзывы клиентов</h1>
|
||||||
<p class="page-subtitle">Мнения клиентов и партнеров</p>
|
<p class="page-subtitle">Реальные отзывы о работе программиста 1С</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="recall-grid">
|
<div class="recall-grid">
|
||||||
{% for p in posts %}
|
{% for p in posts %}
|
||||||
<div class="modern-card fade-in">
|
<div class="modern-card fade-in">
|
||||||
|
|
||||||
|
<!-- Добавляем микроразметку для отзыва -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Review",
|
||||||
|
"itemReviewed": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Услуги программиста 1С"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "{{ p.title }}"
|
||||||
|
},
|
||||||
|
"reviewRating": {
|
||||||
|
"@type": "Rating",
|
||||||
|
"ratingValue": "5",
|
||||||
|
"bestRating": "5"
|
||||||
|
},
|
||||||
|
"datePublished": "{{ p.time_create|date:'Y-m-d' }}",
|
||||||
|
"description": "{{ p.content|striptags|truncatewords:50 }}"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<div class="recall-item">
|
<div class="recall-item">
|
||||||
<div class="recall-header">
|
<div class="recall-header">
|
||||||
<div class="recall-info">
|
<div class="recall-info">
|
||||||
|
|||||||
@ -6,6 +6,7 @@ Sitemap: {{ request.scheme }}://{{ request.get_host }}/sitemap.xml
|
|||||||
|
|
||||||
# Запрещаем служебные разделы
|
# Запрещаем служебные разделы
|
||||||
Disallow: /admin/
|
Disallow: /admin/
|
||||||
|
Disallow: /media/cache/
|
||||||
Disallow: /static/admin/
|
Disallow: /static/admin/
|
||||||
Disallow: /callback/
|
Disallow: /callback/
|
||||||
Disallow: /api/
|
Disallow: /api/
|
||||||
|
|||||||
@ -1,21 +1,40 @@
|
|||||||
{% extends 'programmer/base.html' %}
|
{% extends 'programmer/base.html' %}
|
||||||
{% load django_bootstrap5 %}
|
{% load django_bootstrap5 %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load seo_tags %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="{% static 'programmer/css/solution-accordion.css' %}">
|
<link rel="stylesheet" href="{% static 'programmer/css/solution-accordion.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{title}}</h1>
|
<div class="page-header">
|
||||||
|
<h1 class="page-title">Проекты автоматизации 1С</h1>
|
||||||
|
<p class="page-subtitle">Реализованные решения и кейсы по автоматизации бизнес-процессов</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="improved-list">
|
<div class="improved-list">
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{% for p in posts %}
|
{% for p in posts %}
|
||||||
<li class="fade-in">
|
<li class="modern-card fade-in">
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2>{{p.title}}</h2>
|
<h2>{{p.title}}</h2>
|
||||||
|
|
||||||
|
<!-- Добавляем микроразметку для проекта -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "CreativeWork",
|
||||||
|
"name": "{{ p.title }}",
|
||||||
|
"description": "{{ p.description|striptags|truncatewords:30 }}",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Николай Сердюк"
|
||||||
|
},
|
||||||
|
"datePublished": "{{ p.time_create|date:'Y-m-d' }}"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<div class="solution-accordion">
|
<div class="solution-accordion">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<div class="accordion-header" onclick="toggleAccordion(this)">
|
<div class="accordion-header" onclick="toggleAccordion(this)">
|
||||||
@ -52,6 +71,13 @@
|
|||||||
<p class="first">Опубликовано: {{p.time_create|date:"d.m.Y"}}</p>
|
<p class="first">Опубликовано: {{p.time_create|date:"d.m.Y"}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% empty %}
|
||||||
|
<div class="modern-card text-center fade-in">
|
||||||
|
<h3>🚀 Проекты в разработке</h3>
|
||||||
|
<p class="card-subtitle">Скоро здесь появятся новые кейсы автоматизации</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|||||||
Binary file not shown.
21
OneCprogsite/programmer/templatetags/seo_tags.py
Normal file
21
OneCprogsite/programmer/templatetags/seo_tags.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from django import template
|
||||||
|
from django.utils.html import strip_tags
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def generate_meta_description(obj, default=""):
|
||||||
|
"""Генерирует meta description для объектов"""
|
||||||
|
if hasattr(obj, 'get_seo_description'):
|
||||||
|
return obj.get_seo_description()
|
||||||
|
elif hasattr(obj, 'content'):
|
||||||
|
clean_content = strip_tags(obj.content)[:160]
|
||||||
|
return clean_content + '...' if len(clean_content) > 160 else clean_content
|
||||||
|
return default
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def generate_meta_keywords(obj, default=""):
|
||||||
|
"""Генерирует meta keywords для объектов"""
|
||||||
|
if hasattr(obj, 'get_meta_keywords'):
|
||||||
|
return ', '.join(obj.get_meta_keywords())
|
||||||
|
return default
|
||||||
@ -107,9 +107,9 @@ def index(request):
|
|||||||
context = {
|
context = {
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
'menu': menu,
|
'menu': menu,
|
||||||
'title': "Главная страница",
|
'title': "Программист 1С Николай Сердюк - разработка и сопровождение",
|
||||||
'meta_description': "Профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция и оптимизация систем 1С. Закажите консультацию.",
|
'meta_description': "Профессиональный программист 1С с более чем 10-летним опытом. Разработка, доработка, обновление и интеграция систем 1С. Сопровождение 1С.",
|
||||||
'meta_keywords': "программист 1С, разработка 1С, интеграция 1С, оптимизация 1С, 1С предприятие 8.3",
|
'meta_keywords': "программист 1С, разработка 1С, обновление 1С, сопровождение 1С, интеграция 1С, доработка 1С, 1С предприятие 8.3",
|
||||||
'form': CallbackForm()
|
'form': CallbackForm()
|
||||||
}
|
}
|
||||||
return render(request, 'programmer/index.html', context=context)
|
return render(request, 'programmer/index.html', context=context)
|
||||||
@ -119,9 +119,9 @@ def index(request):
|
|||||||
def about(request):
|
def about(request):
|
||||||
context = {
|
context = {
|
||||||
'menu': menu,
|
'menu': menu,
|
||||||
'title': "Обо мне - Программист 1С",
|
'title': "Программист 1С Николай Сердюк - 10+ лет опыта | Услуги 1С",
|
||||||
'meta_description': "Николай Сердюк - профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция, оптимизация бизнес-процессов.",
|
'meta_description': "Николай Сердюк - сертифицированный программист 1С с 10+ лет опыта. Специализация: обновление 1С, разработка под ключ, интеграция, миграция с 1С 7.7.",
|
||||||
'meta_keywords': "программист 1С Николай Сердюк, опыт работы 1С, компетенции 1С, проекты 1С"
|
'meta_keywords': "программист 1С Николай Сердюк, обновление 1С, разработка 1С под ключ, интеграция 1С, сертифицированный 1С, миграция 1С 7.7"
|
||||||
}
|
}
|
||||||
return render(request, 'programmer/about.html', context=context)
|
return render(request, 'programmer/about.html', context=context)
|
||||||
|
|
||||||
@ -132,7 +132,9 @@ def solution(request):
|
|||||||
context = {
|
context = {
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
'menu': menu,
|
'menu': menu,
|
||||||
'title': "Проекты"
|
'meta_description': "Реализованные проекты по автоматизации 1С: складской учет с ТСД, интеграция с оборудованием, миграция с 1С 7.7. Примеры работ и кейсы.",
|
||||||
|
'meta_keywords': "проекты 1С, автоматизация склада 1С, интеграция ТСД 1С, миграция 1С 7.7, кейсы 1С, примеры работ 1С",
|
||||||
|
'title': "Проекты автоматизации 1С | Реализованные кейсы и решения",
|
||||||
}
|
}
|
||||||
return render(request, 'programmer/solution.html', context=context)
|
return render(request, 'programmer/solution.html', context=context)
|
||||||
|
|
||||||
@ -143,7 +145,9 @@ def ability(request):
|
|||||||
context = {
|
context = {
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
'menu': menu,
|
'menu': menu,
|
||||||
'title': "Компетенции"
|
'title': "Сертификаты и компетенции 1С | Программист 1С Николай Сердюк",
|
||||||
|
'meta_description': "Сертификаты 1С: Профессионал по платформе 8.3 и БП 3.0. Подтвержденная квалификация программиста 1С с сертификатами фирмы 1С.",
|
||||||
|
'meta_keywords': "сертификаты 1С, 1С профессионал, компетенции 1С, квалификация программиста 1С, сертифицированный специалист 1С"
|
||||||
}
|
}
|
||||||
return render(request, 'programmer/competence.html', context=context)
|
return render(request, 'programmer/competence.html', context=context)
|
||||||
|
|
||||||
@ -154,7 +158,9 @@ def recall(request):
|
|||||||
context = {
|
context = {
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
'menu': menu,
|
'menu': menu,
|
||||||
'title': "Отзывы"
|
'title': "Отзывы клиентов о работе программиста 1С | Реальные кейсы",
|
||||||
|
'meta_description': "Реальные отзывы клиентов о работе программиста 1С Николая Сердюка. Отзывы от ООО «РОВЕН-Регионы» и других компаний.",
|
||||||
|
'meta_keywords': "отзывы программист 1С, рекомендации 1С, отзывы клиентов 1С, реальные кейсы 1С, отзыв ООО РОВЕН"
|
||||||
}
|
}
|
||||||
return render(request, 'programmer/recall.html', context=context)
|
return render(request, 'programmer/recall.html', context=context)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @fileOverview CSS for jquery-autocomplete, the jQuery Autocompleter
|
||||||
|
* @author <a href="mailto:dylan@dyve.net">Dylan Verheul</a>
|
||||||
|
* @license MIT | GPL | Apache 2.0, see LICENSE.txt
|
||||||
|
* @see https://github.com/dyve/jquery-autocomplete
|
||||||
|
*/
|
||||||
|
.acResults {
|
||||||
|
padding: 0px;
|
||||||
|
border: 1px solid WindowFrame;
|
||||||
|
background-color: Window;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acResults ul {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
list-style-position: outside;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acResults ul li {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
font: menu;
|
||||||
|
font-size: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acLoading {
|
||||||
|
background : url('../img/indicator.gif') right center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acSelect {
|
||||||
|
background-color: Highlight;
|
||||||
|
color: HighlightText;
|
||||||
|
}
|
||||||
BIN
OneCprogsite/static/django_extensions/img/indicator.gif
Normal file
BIN
OneCprogsite/static/django_extensions/img/indicator.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
116
OneCprogsite/static/django_extensions/js/jquery.ajaxQueue.js
Normal file
116
OneCprogsite/static/django_extensions/js/jquery.ajaxQueue.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Ajax Queue Plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
jQuery.ajaxQueue({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append(html); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxQueue({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append(html); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxSync({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxSync({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<ul style="position: absolute; top: 5px; right: 5px;"></ul>
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Queued Ajax requests.
|
||||||
|
* A new Ajax request won't be started until the previous queued
|
||||||
|
* request has finished.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Synced Ajax requests.
|
||||||
|
* The Ajax request will happen as soon as you call this method, but
|
||||||
|
* the callbacks (success/error/complete) won't fire until all previous
|
||||||
|
* synced requests have been completed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
(function(jQuery) {
|
||||||
|
|
||||||
|
var ajax = jQuery.ajax;
|
||||||
|
|
||||||
|
var pendingRequests = {};
|
||||||
|
|
||||||
|
var synced = [];
|
||||||
|
var syncedData = [];
|
||||||
|
|
||||||
|
jQuery.ajax = function(settings) {
|
||||||
|
// create settings for compatibility with ajaxSetup
|
||||||
|
settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings));
|
||||||
|
|
||||||
|
var port = settings.port;
|
||||||
|
|
||||||
|
switch(settings.mode) {
|
||||||
|
case "abort":
|
||||||
|
if ( pendingRequests[port] ) {
|
||||||
|
pendingRequests[port].abort();
|
||||||
|
}
|
||||||
|
return pendingRequests[port] = ajax.apply(this, arguments);
|
||||||
|
case "queue":
|
||||||
|
var _old = settings.complete;
|
||||||
|
settings.complete = function(){
|
||||||
|
if ( _old )
|
||||||
|
_old.apply( this, arguments );
|
||||||
|
jQuery([ajax]).dequeue("ajax" + port );;
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery([ ajax ]).queue("ajax" + port, function(){
|
||||||
|
ajax( settings );
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "sync":
|
||||||
|
var pos = synced.length;
|
||||||
|
|
||||||
|
synced[ pos ] = {
|
||||||
|
error: settings.error,
|
||||||
|
success: settings.success,
|
||||||
|
complete: settings.complete,
|
||||||
|
done: false
|
||||||
|
};
|
||||||
|
|
||||||
|
syncedData[ pos ] = {
|
||||||
|
error: [],
|
||||||
|
success: [],
|
||||||
|
complete: []
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.error = function(){ syncedData[ pos ].error = arguments; };
|
||||||
|
settings.success = function(){ syncedData[ pos ].success = arguments; };
|
||||||
|
settings.complete = function(){
|
||||||
|
syncedData[ pos ].complete = arguments;
|
||||||
|
synced[ pos ].done = true;
|
||||||
|
|
||||||
|
if ( pos == 0 || !synced[ pos-1 ] )
|
||||||
|
for ( var i = pos; i < synced.length && synced[i].done; i++ ) {
|
||||||
|
if ( synced[i].error ) synced[i].error.apply( jQuery, syncedData[i].error );
|
||||||
|
if ( synced[i].success ) synced[i].success.apply( jQuery, syncedData[i].success );
|
||||||
|
if ( synced[i].complete ) synced[i].complete.apply( jQuery, syncedData[i].complete );
|
||||||
|
|
||||||
|
synced[i] = null;
|
||||||
|
syncedData[i] = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ajax.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
})((typeof window.jQuery == 'undefined' && typeof window.django != 'undefined')
|
||||||
|
? django.jQuery
|
||||||
|
: jQuery
|
||||||
|
);
|
||||||
1152
OneCprogsite/static/django_extensions/js/jquery.autocomplete.js
Normal file
1152
OneCprogsite/static/django_extensions/js/jquery.autocomplete.js
Normal file
File diff suppressed because it is too large
Load Diff
39
OneCprogsite/static/django_extensions/js/jquery.bgiframe.js
Normal file
39
OneCprogsite/static/django_extensions/js/jquery.bgiframe.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*! Copyright (c) 2010 Brandon Aaron (http://brandon.aaron.sh/)
|
||||||
|
* Licensed under the MIT License (LICENSE.txt).
|
||||||
|
*
|
||||||
|
* Version 2.1.2
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($){
|
||||||
|
|
||||||
|
$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) {
|
||||||
|
s = $.extend({
|
||||||
|
top : 'auto', // auto == .currentStyle.borderTopWidth
|
||||||
|
left : 'auto', // auto == .currentStyle.borderLeftWidth
|
||||||
|
width : 'auto', // auto == offsetWidth
|
||||||
|
height : 'auto', // auto == offsetHeight
|
||||||
|
opacity : true,
|
||||||
|
src : 'javascript:false;'
|
||||||
|
}, s);
|
||||||
|
var html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
|
||||||
|
'style="display:block;position:absolute;z-index:-1;'+
|
||||||
|
(s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
|
||||||
|
'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
|
||||||
|
'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
|
||||||
|
'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
|
||||||
|
'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
|
||||||
|
'"/>';
|
||||||
|
return this.each(function() {
|
||||||
|
if ( $(this).children('iframe.bgiframe').length === 0 )
|
||||||
|
this.insertBefore( document.createElement(html), this.firstChild );
|
||||||
|
});
|
||||||
|
} : function() { return this; });
|
||||||
|
|
||||||
|
// old alias
|
||||||
|
$.fn.bgIframe = $.fn.bgiframe;
|
||||||
|
|
||||||
|
function prop(n) {
|
||||||
|
return n && n.constructor === Number ? n + 'px' : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
})((typeof window.jQuery == 'undefined' && typeof window.django != 'undefined') ? django.jQuery : jQuery);
|
||||||
6
OneCprogsite/static/programmer/css/bootstrap.min.css
vendored
Normal file
6
OneCprogsite/static/programmer/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
299
OneCprogsite/static/programmer/css/competence.css
Normal file
299
OneCprogsite/static/programmer/css/competence.css
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
/* competence.css - Стили для страницы компетенций */
|
||||||
|
|
||||||
|
/* Основные стили для страницы компетенций */
|
||||||
|
.competence-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 2rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border-left: 4px solid var(--secondary);
|
||||||
|
transition: var(--transition);
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-item:hover {
|
||||||
|
transform: translateX(8px);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container {
|
||||||
|
width: 280px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2px solid var(--border-light);
|
||||||
|
transition: var(--transition);
|
||||||
|
position: relative;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container:hover {
|
||||||
|
transform: translateY(-4px) scale(1.02);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-description {
|
||||||
|
line-height: 1.7;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-description p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-description p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-hint {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.8));
|
||||||
|
color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: var(--transition);
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container:hover .scan-hint {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для модального окна с изображением компетенций */
|
||||||
|
.modal.competence-modal {
|
||||||
|
background-color: rgba(15, 19, 31, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-content {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
max-width: 95vw;
|
||||||
|
max-height: 95vh;
|
||||||
|
margin: 2% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-bottom: 2px solid var(--border-light);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-body {
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-image {
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Анимации для модального окна */
|
||||||
|
.modal.competence-modal {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-content {
|
||||||
|
transform: scale(0.7);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal.active .modal-content {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Улучшенные тени и границы */
|
||||||
|
.competence-scan-container {
|
||||||
|
border: 2px solid var(--border-light);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container:hover {
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность для мобильных устройств */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.competence-item {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1.5rem;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-title {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-image {
|
||||||
|
max-width: 95vw;
|
||||||
|
max-height: 70vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-content {
|
||||||
|
margin: 10% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.competence-item {
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-scan-container {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-content {
|
||||||
|
margin: 5% auto;
|
||||||
|
max-width: 98vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-body {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для светлой темы */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.modal.competence-modal {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.competence-modal .modal-header {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-hint {
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.7));
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-item {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-description {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Улучшенные стили для сетки компетенций */
|
||||||
|
.competence-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-grid .modern-card {
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.competence-grid .modern-card::before {
|
||||||
|
height: 4px;
|
||||||
|
background: var(--gradient-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Анимации появления */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeInUp 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(40px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
228
OneCprogsite/static/programmer/css/recall.css
Normal file
228
OneCprogsite/static/programmer/css/recall.css
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/* recall.css - Стили для страницы отзывов */
|
||||||
|
|
||||||
|
/* Основные стили для страницы отзывов */
|
||||||
|
.recall-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-container {
|
||||||
|
width: 280px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2px solid var(--border-light);
|
||||||
|
transition: var(--transition);
|
||||||
|
position: relative;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-container:hover {
|
||||||
|
transform: translateY(-4px) scale(1.02);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
line-height: 1.7;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-text p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-text p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-hint {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.8));
|
||||||
|
color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: var(--transition);
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-container:hover .scan-hint {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для модального окна с изображением */
|
||||||
|
.modal.image-modal {
|
||||||
|
background-color: rgba(15, 19, 31, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-content {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
max-width: 95vw;
|
||||||
|
max-height: 95vh;
|
||||||
|
margin: 2% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-bottom: 2px solid var(--border-light);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-body {
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-image {
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Анимации для модального окна */
|
||||||
|
.modal.image-modal {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-content {
|
||||||
|
transform: scale(0.7);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal.active .modal-content {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Улучшенные тени и границы */
|
||||||
|
.recall-scan-container {
|
||||||
|
border: 2px solid var(--border-light);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-container:hover {
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность для мобильных устройств */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.recall-content {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recall-scan-wrapper {
|
||||||
|
order: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-image {
|
||||||
|
max-width: 95vw;
|
||||||
|
max-height: 70vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-content {
|
||||||
|
margin: 10% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.recall-scan-container {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-content {
|
||||||
|
margin: 5% auto;
|
||||||
|
max-width: 98vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-body {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для светлой темы */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.modal.image-modal {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.image-modal .modal-header {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-hint {
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.7));
|
||||||
|
}
|
||||||
|
}
|
||||||
67
OneCprogsite/static/programmer/css/solution-accordion.css
Normal file
67
OneCprogsite/static/programmer/css/solution-accordion.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* solution-accordion.css */
|
||||||
|
.solution-accordion {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-item {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: 2px solid var(--border-light);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-item:hover {
|
||||||
|
border-color: var(--primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header {
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
transition: var(--transition);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header:hover {
|
||||||
|
background: var(--primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-content {
|
||||||
|
padding: 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-content.active {
|
||||||
|
padding: 1.5rem;
|
||||||
|
max-height: 5000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-icon {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header.active .accordion-icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-content p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-content p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
1007
OneCprogsite/static/programmer/css/styles.css
Normal file
1007
OneCprogsite/static/programmer/css/styles.css
Normal file
File diff suppressed because it is too large
Load Diff
1473
OneCprogsite/static/programmer/css/styles_dark.css
Normal file
1473
OneCprogsite/static/programmer/css/styles_dark.css
Normal file
File diff suppressed because it is too large
Load Diff
1406
OneCprogsite/static/programmer/css/styles_w.css
Normal file
1406
OneCprogsite/static/programmer/css/styles_w.css
Normal file
File diff suppressed because it is too large
Load Diff
73
OneCprogsite/static/programmer/js/competence.js
Normal file
73
OneCprogsite/static/programmer/js/competence.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// competence.js - Скрипты для страницы компетенций
|
||||||
|
function openCompetenceModal(imageUrl, title) {
|
||||||
|
console.log('Opening competence modal with:', imageUrl);
|
||||||
|
const modal = document.getElementById('competenceModal');
|
||||||
|
const modalImg = document.getElementById('competenceModalImage');
|
||||||
|
const modalTitle = document.getElementById('competenceModalTitle');
|
||||||
|
|
||||||
|
if (modal && modalImg) {
|
||||||
|
modal.style.display = "block";
|
||||||
|
modalImg.src = imageUrl;
|
||||||
|
if (title && modalTitle) {
|
||||||
|
modalTitle.textContent = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем класс для анимации
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.classList.add('active');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Подстраиваем размер изображения
|
||||||
|
adjustCompetenceModalImageSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCompetenceModal() {
|
||||||
|
const modal = document.getElementById('competenceModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustCompetenceModalImageSize() {
|
||||||
|
const modalImg = document.getElementById('competenceModalImage');
|
||||||
|
|
||||||
|
if (modalImg) {
|
||||||
|
const maxWidth = window.innerWidth * 0.9;
|
||||||
|
const maxHeight = window.innerHeight * 0.8;
|
||||||
|
|
||||||
|
modalImg.style.maxWidth = `${maxWidth}px`;
|
||||||
|
modalImg.style.maxHeight = `${maxHeight}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация после загрузки DOM
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Закрытие модального окна при клике вне изображения
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
const modal = document.getElementById('competenceModal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeCompetenceModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Закрытие по ESC
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeCompetenceModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Адаптация размера изображения при изменении размера окна
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
const modalImg = document.getElementById('competenceModalImage');
|
||||||
|
if (modalImg && modalImg.src) {
|
||||||
|
adjustCompetenceModalImageSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Competence page scripts initialized');
|
||||||
|
});
|
||||||
94
OneCprogsite/static/programmer/js/mobile-menu.js
Normal file
94
OneCprogsite/static/programmer/js/mobile-menu.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Mobile Menu Script
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log('Mobile menu script loaded'); // Для отладки
|
||||||
|
|
||||||
|
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
|
||||||
|
const mobileMenuClose = document.getElementById('mobileMenuClose');
|
||||||
|
const mobileMenuOverlay = document.getElementById('mobileMenuOverlay');
|
||||||
|
const mobileMenu = document.getElementById('mobileMenu');
|
||||||
|
const mobileThemeToggle = document.getElementById('mobile-theme-toggle');
|
||||||
|
const mainThemeToggle = document.getElementById('theme-toggle');
|
||||||
|
|
||||||
|
// Проверяем, что элементы существуют
|
||||||
|
if (!mobileMenuBtn || !mobileMenu) {
|
||||||
|
console.error('Mobile menu elements not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Mobile menu elements found:', {
|
||||||
|
mobileMenuBtn,
|
||||||
|
mobileMenuClose,
|
||||||
|
mobileMenuOverlay,
|
||||||
|
mobileMenu,
|
||||||
|
mobileThemeToggle,
|
||||||
|
mainThemeToggle
|
||||||
|
});
|
||||||
|
|
||||||
|
// Открытие мобильного меню
|
||||||
|
mobileMenuBtn.addEventListener('click', function() {
|
||||||
|
console.log('Opening mobile menu');
|
||||||
|
mobileMenu.classList.add('active');
|
||||||
|
mobileMenuOverlay.style.display = 'block';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Закрытие мобильного меню
|
||||||
|
function closeMobileMenu() {
|
||||||
|
console.log('Closing mobile menu');
|
||||||
|
mobileMenu.classList.remove('active');
|
||||||
|
mobileMenuOverlay.style.display = 'none';
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileMenuClose) {
|
||||||
|
mobileMenuClose.addEventListener('click', closeMobileMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileMenuOverlay) {
|
||||||
|
mobileMenuOverlay.addEventListener('click', closeMobileMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрытие меню при клике на ссылку
|
||||||
|
const mobileNavLinks = document.querySelectorAll('.mobile-nav-link');
|
||||||
|
mobileNavLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', closeMobileMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Синхронизация переключателей темы
|
||||||
|
function syncThemeToggles() {
|
||||||
|
if (mobileThemeToggle && mainThemeToggle) {
|
||||||
|
mobileThemeToggle.checked = mainThemeToggle.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainThemeToggle) {
|
||||||
|
mainThemeToggle.addEventListener('change', function() {
|
||||||
|
console.log('Main theme toggle changed:', this.checked);
|
||||||
|
syncThemeToggles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileThemeToggle) {
|
||||||
|
mobileThemeToggle.addEventListener('change', function() {
|
||||||
|
console.log('Mobile theme toggle changed:', this.checked);
|
||||||
|
if (mainThemeToggle) {
|
||||||
|
mainThemeToggle.checked = this.checked;
|
||||||
|
// Триггерим событие change
|
||||||
|
const event = new Event('change');
|
||||||
|
mainThemeToggle.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация синхронизации
|
||||||
|
syncThemeToggles();
|
||||||
|
|
||||||
|
// Закрытие меню по ESC
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeMobileMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Mobile menu script initialized successfully');
|
||||||
|
});
|
||||||
74
OneCprogsite/static/programmer/js/recall.js
Normal file
74
OneCprogsite/static/programmer/js/recall.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// recall.js - Скрипты для страницы отзывов
|
||||||
|
function openModal(imageUrl, title) {
|
||||||
|
console.log('Opening modal with:', imageUrl);
|
||||||
|
const modal = document.getElementById('imageModal');
|
||||||
|
const modalImg = document.getElementById('modalImage');
|
||||||
|
const modalTitle = document.getElementById('modalTitle');
|
||||||
|
|
||||||
|
if (modal && modalImg) {
|
||||||
|
modal.style.display = "block";
|
||||||
|
modalImg.src = imageUrl;
|
||||||
|
if (title && modalTitle) {
|
||||||
|
modalTitle.textContent = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем класс для анимации
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.classList.add('active');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Подстраиваем размер изображения
|
||||||
|
adjustModalImageSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
const modal = document.getElementById('imageModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustModalImageSize() {
|
||||||
|
const modalImg = document.getElementById('modalImage');
|
||||||
|
const modalContent = document.querySelector('.modal-content');
|
||||||
|
|
||||||
|
if (modalImg && modalContent) {
|
||||||
|
const maxWidth = window.innerWidth * 0.9;
|
||||||
|
const maxHeight = window.innerHeight * 0.8;
|
||||||
|
|
||||||
|
modalImg.style.maxWidth = `${maxWidth}px`;
|
||||||
|
modalImg.style.maxHeight = `${maxHeight}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация после загрузки DOM
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Закрытие модального окна при клике вне изображения
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
const modal = document.getElementById('imageModal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Закрытие по ESC
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Адаптация размера изображения при изменении размера окна
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
const modalImg = document.getElementById('modalImage');
|
||||||
|
if (modalImg && modalImg.src) {
|
||||||
|
adjustModalImageSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Recall page scripts initialized');
|
||||||
|
});
|
||||||
46
OneCprogsite/static/programmer/js/solution-accordion.js
Normal file
46
OneCprogsite/static/programmer/js/solution-accordion.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// solution-accordion.js
|
||||||
|
function toggleAccordion(header) {
|
||||||
|
const content = header.nextElementSibling;
|
||||||
|
const icon = header.querySelector('.accordion-icon');
|
||||||
|
|
||||||
|
// Переключаем только текущий аккордеон
|
||||||
|
header.classList.toggle('active');
|
||||||
|
content.classList.toggle('active');
|
||||||
|
icon.style.transform = header.classList.contains('active') ? 'rotate(180deg)' : 'rotate(0deg)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для открытия всех аккордеонов
|
||||||
|
function expandAll() {
|
||||||
|
document.querySelectorAll('.accordion-content').forEach(content => {
|
||||||
|
content.classList.add('active');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.accordion-header').forEach(header => {
|
||||||
|
header.classList.add('active');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.accordion-icon').forEach(icon => {
|
||||||
|
icon.style.transform = 'rotate(180deg)';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для закрытия всех аккордеонов
|
||||||
|
function collapseAll() {
|
||||||
|
document.querySelectorAll('.accordion-content').forEach(content => {
|
||||||
|
content.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.accordion-header').forEach(header => {
|
||||||
|
header.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.accordion-icon').forEach(icon => {
|
||||||
|
icon.style.transform = 'rotate(0deg)';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Автоматически открываем первый аккордеон в каждой карточке при загрузке
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('.content-card').forEach(card => {
|
||||||
|
const firstAccordion = card.querySelector('.accordion-header');
|
||||||
|
if (firstAccordion) {
|
||||||
|
toggleAccordion(firstAccordion);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
68
OneCprogsite/static/programmer/js/theme-switcher.js
Normal file
68
OneCprogsite/static/programmer/js/theme-switcher.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Theme Switcher Script
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const themeToggle = document.getElementById('theme-toggle');
|
||||||
|
const mobileThemeToggle = document.getElementById('mobile-theme-toggle');
|
||||||
|
const themeCSS = document.getElementById('theme-css');
|
||||||
|
|
||||||
|
// Проверяем сохраненную тему в localStorage
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
|
||||||
|
// Устанавливаем светлую тему по умолчанию
|
||||||
|
if (savedTheme === 'dark') {
|
||||||
|
switchToDarkTheme();
|
||||||
|
} else {
|
||||||
|
switchToLightTheme(); // Светлая тема по умолчанию
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик переключения темы для десктопного переключателя
|
||||||
|
if (themeToggle) {
|
||||||
|
themeToggle.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
switchToLightTheme();
|
||||||
|
} else {
|
||||||
|
switchToDarkTheme();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик переключения темы для мобильного переключателя
|
||||||
|
if (mobileThemeToggle) {
|
||||||
|
mobileThemeToggle.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
switchToLightTheme();
|
||||||
|
} else {
|
||||||
|
switchToDarkTheme();
|
||||||
|
}
|
||||||
|
// Синхронизируем оба переключателя
|
||||||
|
if (themeToggle) {
|
||||||
|
themeToggle.checked = this.checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchToLightTheme() {
|
||||||
|
themeCSS.href = themeCSS.href.replace('styles_dark.css', 'styles_w.css');
|
||||||
|
if (themeToggle) themeToggle.checked = true;
|
||||||
|
if (mobileThemeToggle) mobileThemeToggle.checked = true;
|
||||||
|
localStorage.setItem('theme', 'light');
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchToDarkTheme() {
|
||||||
|
themeCSS.href = themeCSS.href.replace('styles_w.css', 'styles_dark.css');
|
||||||
|
if (themeToggle) themeToggle.checked = false;
|
||||||
|
if (mobileThemeToggle) mobileThemeToggle.checked = false;
|
||||||
|
localStorage.setItem('theme', 'dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Синхронизация переключателей при загрузке
|
||||||
|
if (themeToggle && mobileThemeToggle) {
|
||||||
|
mobileThemeToggle.checked = themeToggle.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка ошибок загрузки CSS
|
||||||
|
themeCSS.onerror = function() {
|
||||||
|
console.error('Ошибка загрузки CSS файла темы');
|
||||||
|
// Восстанавливаем светлую тему по умолчанию при ошибке
|
||||||
|
themeCSS.href = themeCSS.href.replace('styles_dark.css', 'styles_w.css');
|
||||||
|
};
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user