Merge pull request 'fix_test' (#11) from fix_test into main продукты
Some checks failed
Auto-update README / update-readme (push) Has been cancelled

Reviewed-on: #11
This commit is contained in:
Николай Сердюк 2026-06-19 16:59:37 +03:00
commit 926f20707a
81 changed files with 2650 additions and 11413 deletions

View File

@ -70,6 +70,10 @@ if not DEBUG:
LANGUAGE_COOKIE_HTTPONLY = True
LANGUAGE_COOKIE_SAMESITE = 'Lax'
# Redis
CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL', 'redis://localhost:6379/0')
CELERY_RESULT_BACKEND = os.environ.get('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0')
CSRF_TRUSTED_ORIGINS = [
'https://nikdizell.ru',
'https://www.nikdizell.ru',
@ -94,6 +98,8 @@ INSTALLED_APPS = [
'django_ckeditor_5',
'captcha',
'turnstile',
'products',
'background_task',
]
MIDDLEWARE = [
@ -132,6 +138,7 @@ TEMPLATES = [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'programmer.context_processors.contact_info',
'programmer.context_processors.callback_form',
],
},
},
@ -286,5 +293,5 @@ CAPTCHA_LENGTH = 6
CAPTCHA_FONT_SIZE = 30
CAPTCHA_IMAGE_SIZE = (150, 50)
TURNSTILE_SITEKEY = '0x4AAAAAAC12NGPpc4TutFWA'
TURNSTILE_SECRET = '0x4AAAAAAC12NCpzKHKE09JaXRDv0smrSAU'
TURNSTILE_SITEKEY = os.getenv('TURNSTILE_SITEKEY')
TURNSTILE_SECRET = os.getenv('TURNSTILE_SECRET')

View File

@ -26,6 +26,7 @@ urlpatterns = [
path('blog/', include('blog.urls')),
path('ckeditor5/', include('django_ckeditor_5.urls')),
path('captcha/', include('captcha.urls')),
path('products/', include('products.urls', namespace='products')),
# path('', index, name='home'),
# path('about/', about, name='about'),
# path('solution/', solution, name='solution'),

View File

@ -5,9 +5,11 @@ from django.utils.html import strip_tags
class CommentForm(forms.ModelForm):
parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
class Meta:
model = Comment
fields = ['author_name', 'author_email', 'content']
fields = ['author_name', 'author_email', 'content', 'parent_id']
widgets = {
'author_name': forms.TextInput(attrs={'class': 'form-input', 'placeholder': 'Ваше имя'}),
'author_email': forms.EmailInput(attrs={'class': 'form-input', 'placeholder': 'Ваш email'}),

View File

@ -81,6 +81,18 @@ class Comment(models.Model):
)
author_name = models.CharField(max_length=100, verbose_name="Имя")
author_email = models.EmailField(verbose_name="Email")
user = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, blank=True,
verbose_name="Пользователь"
)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='replies',
verbose_name='Ответ на'
)
content = models.TextField(verbose_name="Комментарий")
is_moderated = models.BooleanField(default=False, verbose_name="Промодерировано")
time_create = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")

View File

@ -2,6 +2,11 @@ from typing import Optional, List
from django.db.models import F, QuerySet
from django.core.cache import cache
from .models import Article, Category, Comment
from django.contrib.auth import get_user_model
User = get_user_model()
def get_published_articles(category_slug: Optional[str] = None) -> QuerySet[Article]:
"""
@ -29,13 +34,16 @@ def increment_article_views(article: Article) -> None:
"""Увеличивает счётчик просмотров статьи."""
Article.objects.filter(pk=article.pk).update(views_count=F('views_count') + 1)
def add_comment_to_article(article: Article, data: dict) -> Comment:
def add_comment_to_article(article: Article, data: dict, user: User = None, parent: Comment = None) -> Comment:
"""Создание комментария к статье (без модерации)."""
comment = Comment.objects.create(
article=article,
author_name=data['author_name'],
author_email=data['author_email'],
content=data['content']
content=data['content'],
user=user,
parent=parent,
is_moderated=False
)
# Здесь можно отправить уведомление администратору
return comment

13
blog/signals.py Normal file
View File

@ -0,0 +1,13 @@
# blog/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Article
from programmer.models import Profile
from django.core.mail import send_mail
@receiver(post_save, sender=Article)
def notify_about_new_article(sender, instance, created, **kwargs):
if created and instance.is_published:
profiles = Profile.objects.filter(notify_new_articles=True, user__is_active=True)
emails = [p.user.email for p in profiles if p.user.email]
# отправка письма (можно асинхронно)

View File

@ -88,3 +88,28 @@
background: none;
color: inherit;
}
.comment-item {
border-left: 2px solid var(--border-light);
padding-left: 15px;
margin-bottom: 15px;
}
.comment-meta {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.badge-warning {
background-color: #ffc107;
color: #212529;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
}
.comment-content {
margin: 5px 0;
}
.reply-btn {
margin-top: 5px;
}

View File

@ -2,6 +2,7 @@
{% load static %}
{% load django_bootstrap5 %}
{% load seo_tags %}
{% load blog_extras %}
{% block extra_css %}
<link id="theme-css-1c" rel="stylesheet" href="{% static 'programmer/css/1c-light.min.css' %}">
@ -29,26 +30,19 @@
<!-- Комментарии -->
<h3>Комментарии</h3>
{% for comment in moderated_comments %}
<div class="modern-card" style="margin-bottom: 1rem;">
<p><strong>{{ comment.author_name }}</strong> ({{ comment.time_create|date:"d.m.Y H:i" }})</p>
<p>{{ comment.content|linebreaks }}</p>
{% if not comment.is_moderated %}
<p><em>Комментарий ожидает модерации</em></p>
{% endif %}
</div>
{% empty %}
<p>Пока нет комментариев. Будьте первым!</p>
{% endfor %}
<div id="comments-section">
{% show_comments comments=root_comments %}
</div>
<!-- Форма добавления комментария -->
<div class="modern-card">
<div class="modern-card" id="comment-form">
<h4>Добавить комментарий</h4>
{% if user_is_authenticated %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
{{ form.parent_id }}
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
{% else %}
@ -73,7 +67,6 @@
</div>
</div>
<!-- Скрипт для модального окна -->
<script src="{% static 'programmer/js/recall.js' %}"></script>
{% endblock %}
@ -81,18 +74,41 @@
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Находим все изображения внутри .content-card (основной контент статьи)
// Клик по изображениям в контенте
document.querySelectorAll('.content-card img').forEach(function(img) {
// Игнорируем, если изображение уже обёрнуто в ссылку
if (img.closest('a')) return;
// Меняем курсор, чтобы показать, что изображение кликабельно
img.style.cursor = 'pointer';
img.addEventListener('click', function() {
// Вызываем глобальную функцию openModal из recall.js
ImageModal.open(this.src, this.alt || 'Изображение из статьи');
});
});
// Обработка кнопок "Ответить" (делегирование)
document.getElementById('comments-section').addEventListener('click', function(e) {
const btn = e.target.closest('.reply-btn');
if (!btn) return;
e.preventDefault();
const parentId = btn.dataset.commentId;
const author = btn.dataset.author;
// Находим текст комментария
const commentItem = btn.closest('.comment-item');
const contentDiv = commentItem.querySelector('.comment-content');
let quotedText = contentDiv.textContent.trim();
// Формируем цитату
const quote = `> @${author}: ${quotedText}\n\n`;
const textarea = document.getElementById('id_content');
textarea.value = quote;
// Устанавливаем parent_id
document.getElementById('id_parent_id').value = parentId;
// Фокус на поле ввода и прокрутка к форме
textarea.focus();
document.getElementById('comment-form').scrollIntoView({ behavior: 'smooth' });
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% load blog_extras %}
{% load static %}
{% for comment in comments %}
<div class="comment-item" style="margin-left: {{ depth|add:20 }}px; border-left: 2px solid var(--border-light); padding-left: 15px; margin-bottom: 15px;">
<div class="comment-meta">
<strong>{{ comment.author_name }}</strong>
<span style="color: var(--text-light); font-size: 0.9rem;">#{{ comment.id }}</span>
<span style="color: var(--text-light); font-size: 0.9rem;">{{ comment.time_create|date:"d.m.Y H:i" }}</span>
{% if not comment.is_moderated %}
<span class="badge badge-warning">На модерации</span>
{% endif %}
</div>
<div class="comment-content" style="margin: 5px 0;">
{{ comment.content|linebreaks }}
</div>
{% if user.is_authenticated %}
<button class="btn btn-sm btn-outline-primary reply-btn"
data-comment-id="{{ comment.id }}"
data-author="{{ comment.author_name }}">
Ответить
</button>
{% endif %}
{% with replies=comment.replies.all %}
{% if replies %}
<div class="replies" style="margin-top: 10px;">
{% show_comments replies depth=forloop.parentloop.counter0|add:depth %}
</div>
{% endif %}
{% endwith %}
</div>
{% empty %}
<p>Комментариев пока нет.</p>
{% endfor %}

View File

@ -1,10 +1,12 @@
from django import template
from django.utils.html import strip_tags
from blog.models import Comment
import html
register = template.Library()
@register.filter
def clean_html(value):
"""Удаляет HTML-теги и заменяет сущности на символы."""
@ -12,3 +14,22 @@ def clean_html(value):
value = html.unescape(value)
# Удаляем все HTML-теги
return strip_tags(value)
@register.inclusion_tag('blog/includes/comment_tree.html', takes_context=True)
def show_comments(context, comments, depth=0):
request = context.get('request')
user = request.user if request and request.user.is_authenticated else None
filtered = []
for c in comments:
if c.is_moderated:
filtered.append(c)
elif user and c.user == user:
filtered.append(c)
return {
'comments': filtered,
'depth': depth,
'user': user
}

View File

@ -4,7 +4,7 @@ from django.urls import reverse_lazy
from django.contrib import messages
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from .models import Article, Category
from .models import Article, Category, Comment
from .services import (
get_published_articles, get_article_by_slug,
increment_article_views, add_comment_to_article
@ -103,9 +103,21 @@ class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
form = CommentForm(request.POST, request=request)
if form.is_valid():
parent_id = form.cleaned_data.get('parent_id')
parent = None
if parent_id:
try:
parent = Comment.objects.get(id=parent_id, article=self.object)
except Comment.DoesNotExist:
pass
add_comment_to_article(
article=self.object,
data=form.cleaned_data
data=form.cleaned_data,
user=request.user,
parent=parent
)
messages.success(request, "✅ Ваш комментарий отправлен на модерацию.")
return redirect(self.object.get_absolute_url())
@ -121,6 +133,9 @@ class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
if 'form' not in kwargs:
context['form'] = CommentForm(request=self.request)
root_comments = self.object.comments.filter(parent__isnull=True, is_moderated=True)
context['root_comments'] = root_comments
context.update({
'moderated_comments': self.object.comments.filter(is_moderated=True),
'user_is_authenticated': self.request.user.is_authenticated,

0
products/__init__.py Normal file
View File

134
products/admin.py Normal file
View File

@ -0,0 +1,134 @@
from django.contrib import admin
from django.utils.html import format_html
from django.shortcuts import render
from django_ckeditor_5.widgets import CKEditor5Widget
from .models import ProductCategory, Configuration, Product, Order
from programmer.utils.email_notifications import send_multiple_order_notifications, send_multiple_order_notifications_async
@admin.register(ProductCategory)
class ProductCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'description')
prepopulated_fields = {'slug': ('name',)}
search_fields = ('name',)
@admin.register(Configuration)
class ConfigurationAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'description')
prepopulated_fields = {'slug': ('name',)}
search_fields = ('name',)
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
form.base_fields['content'].widget = CKEditor5Widget(config_name='default')
return form
list_display = ('title', 'category', 'price', 'is_published', 'time_create', 'configurations_list')
list_filter = ('is_published', 'category', 'configurations', 'time_create')
search_fields = ('title', 'short_description', 'content')
prepopulated_fields = {'slug': ('title',)}
filter_horizontal = ('configurations',)
date_hierarchy = 'time_create'
actions = ['make_published', 'make_unpublished']
fieldsets = (
(None, {
'fields': ('title', 'slug', 'category', 'price', 'is_published')
}),
('Описания', {
'fields': ('short_description', 'content', 'image'),
'description': 'Краткое обычный текст для карточки; полное HTML-редактор для детальной страницы.'
}),
('Совместимость', {
'fields': ('configurations',),
}),
('SEO', {
'fields': ('meta_description',),
'classes': ('collapse',),
}),
)
def configurations_list(self, obj):
return ", ".join(c.name for c in obj.configurations.all())
configurations_list.short_description = "Конфигурации"
def make_published(self, request, queryset):
queryset.update(is_published=True)
make_published.short_description = "Опубликовать выбранные продукты"
def make_unpublished(self, request, queryset):
queryset.update(is_published=False)
make_unpublished.short_description = "Снять с публикации"
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'product', 'name', 'phone', 'created_at', 'is_processed', 'notification_sent')
list_filter = ('is_processed', 'product', 'configuration', 'notification_sent')
search_fields = ('name', 'phone', 'email')
list_editable = ('is_processed',)
readonly_fields = ('created_at',)
actions = ['resend_order_notification'] # добавляем действие в выпадающий список
def notification_badge(self, obj):
# Если в модели Order добавите поле notification_sent, можно показывать иконку
# Пока просто заглушка всегда зелёная галочка (уведомление считается отправленным при создании)
return format_html('<span style="color:green;">✓</span>')
notification_badge.short_description = 'Уведомление'
def resend_order_notification(self, request, queryset):
"""
Переотправляет уведомления для выбранных заказов.
"""
# count = send_multiple_order_notifications(queryset)
count = send_multiple_order_notifications_async(queryset)
self.message_user(request, f'Уведомления отправлены для {count} заказов.')
resend_order_notification.short_description = 'Переотправить email уведомления'
# Опционально: добавить кастомную кнопку на страницу списка
def get_urls(self):
from django.urls import path
from django.shortcuts import render
from django.contrib import messages
urls = super().get_urls()
custom_urls = [
path(
'order-stats/',
self.admin_site.admin_view(self.order_stats_view),
name='order_stats',
),
]
return custom_urls + urls
def notification_badge(self, obj):
if obj.notification_sent:
return format_html('<span style="color:green;">✓</span>')
return format_html('<span style="color:red;">✗</span>')
def order_stats_view(self, request):
from .models import Order
from django.utils import timezone
today = timezone.now().date()
week_ago = today - timezone.timedelta(days=7)
stats = {
'total': Order.objects.count(),
'today': Order.objects.filter(created_at__date=today).count(),
'week': Order.objects.filter(created_at__date__gte=week_ago).count(),
'unprocessed': Order.objects.filter(is_processed=False).count(),
}
context = {
**self.admin_site.each_context(request),
'title': 'Статистика заказов',
'stats': stats,
}
return render(request, 'admin/order_stats.html', context)

6
products/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'products'

27
products/forms.py Normal file
View File

@ -0,0 +1,27 @@
from django import forms
from .models import Order, Configuration
class OrderForm(forms.ModelForm):
# Поле выбора конфигурации. Заполним его динамически во вьюхе.
configuration = forms.ModelChoiceField(
queryset=Configuration.objects.none(),
required=True,
label='Конфигурация 1С',
widget=forms.Select(attrs={'class': 'form-input'})
)
class Meta:
model = Order
fields = ['name', 'phone', 'email', 'comment', 'configuration']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-input', 'placeholder': 'Ваше имя'}),
'phone': forms.TextInput(attrs={'class': 'form-input', 'placeholder': '+7 (___) ___-__-__'}),
'email': forms.EmailInput(attrs={'class': 'form-input', 'placeholder': 'your@email.com'}),
'comment': forms.Textarea(attrs={'class': 'form-textarea', 'rows': 2, 'placeholder': 'Доп. информация, например: релиз концигурации'}),
'configuration': forms.Select(attrs={'class': 'form-input'}),
}
labels = {
'name': 'Имя *',
'phone': 'Телефон *',
'email': 'Email *',
}

View File

131
products/models.py Normal file
View File

@ -0,0 +1,131 @@
from django.db import models
from django.urls import reverse
from django.utils.html import strip_tags
from django.conf import settings
import re
class ProductCategory(models.Model):
"""Категория продуктов"""
name = models.CharField(max_length=100, unique=True, verbose_name="Название")
slug = models.SlugField(max_length=120, unique=True, verbose_name="URL")
description = models.TextField(blank=True, verbose_name="Описание")
class Meta:
verbose_name = "Категория продуктов"
verbose_name_plural = "Категории продуктов"
def __str__(self):
return self.name
class Configuration(models.Model):
"""Конфигурация 1С, к которой относится продукт"""
name = models.CharField(max_length=200, unique=True, verbose_name="Название")
slug = models.SlugField(max_length=220, unique=True, verbose_name="URL")
description = models.TextField(blank=True, verbose_name="Описание")
class Meta:
verbose_name = "Конфигурация 1С"
verbose_name_plural = "Конфигурации 1С"
def __str__(self):
return self.name
class Product(models.Model):
"""Программный продукт / модуль 1С"""
title = models.CharField(max_length=200, verbose_name="Название")
slug = models.SlugField(max_length=220, unique=True, verbose_name="URL")
category = models.ForeignKey(
ProductCategory,
on_delete=models.PROTECT,
related_name="products",
verbose_name="Категория"
)
short_description = models.TextField(
verbose_name="Краткое описание",
help_text="Отображается в списке продуктов"
)
content = models.TextField(verbose_name="Полное описание")
price = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name="Цена",
help_text="Будет видна только зарегистрированным пользователям"
)
image = models.ImageField(
upload_to="products/%Y/%m/%d/",
blank=True,
verbose_name="Изображение"
)
configurations = models.ManyToManyField(
Configuration,
blank=True,
related_name="products",
verbose_name="Совместимые конфигурации"
)
is_published = models.BooleanField(default=True, verbose_name="Опубликовано")
time_create = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
time_update = models.DateTimeField(auto_now=True, verbose_name="Дата изменения")
meta_description = models.CharField(
max_length=160,
blank=True,
verbose_name="Meta-описание",
help_text="Краткое описание для поисковых систем (до 160 символов)"
)
class Meta:
verbose_name = "Продукт"
verbose_name_plural = "Продукты"
ordering = ["-time_create"]
indexes = [
models.Index(fields=["-time_create", "is_published"]),
]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("products:product_detail", kwargs={"slug": self.slug})
def get_seo_title(self):
return f"{self.title} | Продукты С.Н.А. Технологии"
def get_seo_description(self):
if self.meta_description:
return self.meta_description
clean = re.sub(r'\s+', ' ', strip_tags(self.short_description)).strip()
return clean[:157] + '' if len(clean) > 160 else clean
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='Продукт')
configuration = models.ForeignKey(
Configuration,
on_delete=models.SET_NULL,
null=True, blank=True,
verbose_name='Конфигурация 1С'
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True, blank=True,
verbose_name='Пользователь'
)
name = models.CharField(max_length=100, verbose_name='Имя')
phone = models.CharField(max_length=20, verbose_name='Телефон')
email = models.EmailField(verbose_name='Email')
address = models.TextField(blank=True, verbose_name='Адрес доставки')
comment = models.TextField(blank=True, verbose_name='Комментарий')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Дата заказа')
is_processed = models.BooleanField(default=False, verbose_name='Обработан')
notification_sent = models.BooleanField(default=False, verbose_name='Уведомление отправлено')
class Meta:
verbose_name = 'Заказ'
verbose_name_plural = 'Заказы'
ordering = ['-created_at']
def __str__(self):
return f'Заказ #{self.id} {self.product.title} от {self.name}'

View File

@ -0,0 +1,237 @@
/*
* ck_content.css общие стили для CKEditor-контента
* Используется в блоге (.article-content) и в продуктах (.product-description).
* Подключать вместо article_content.css / product_content.css.
*
* Размещение: static/core/css/ck_content.css (или аналогичное shared-место)
*/
/* ===== Базовый контейнер ===== */
.article-content,
.product-description {
font-size: 1rem;
line-height: 1.7;
color: var(--text-primary);
}
/* ===== Заголовки ===== */
.article-content h1, .product-description h1,
.article-content h2, .product-description h2,
.article-content h3, .product-description h3,
.article-content h4, .product-description h4,
.article-content h5, .product-description h5,
.article-content h6, .product-description h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
font-weight: 600;
line-height: 1.25;
color: var(--text-primary);
}
.article-content h1, .product-description h1 { font-size: 2rem; }
.article-content h2, .product-description h2 { font-size: 1.75rem; }
.article-content h3, .product-description h3 { font-size: 1.5rem; }
.article-content h4, .product-description h4 { font-size: 1.25rem; }
.article-content h5, .product-description h5 { font-size: 1.1rem; }
.article-content h6, .product-description h6 { font-size: 1rem; }
/* ===== Абзацы ===== */
.article-content p,
.product-description p {
margin-bottom: 1.2em;
}
/* ===== Списки ===== */
.article-content ul, .product-description ul,
.article-content ol, .product-description ol {
margin-bottom: 1rem;
padding-left: 2rem;
}
.article-content li,
.product-description li {
margin-bottom: 0.3rem;
}
/* ===== Цитаты ===== */
.article-content blockquote,
.product-description blockquote {
border-left: 4px solid var(--primary);
padding: 0.5rem 1rem;
background: var(--bg-secondary);
margin: 1.25rem 0;
font-style: italic;
color: var(--text-secondary);
}
/* ===== Горизонтальная линия ===== */
.article-content hr,
.product-description hr {
border: none;
border-top: 2px solid var(--border-light);
margin: 2em 0;
}
/* ===== Таблицы ===== */
.article-content table,
.product-description table {
width: 100%;
border-collapse: collapse;
margin: 1.25rem 0;
font-size: 0.95rem;
}
.article-content th, .product-description th,
.article-content td, .product-description td {
border: 1px solid var(--border-light);
padding: 0.6rem 0.8rem;
text-align: left;
}
.article-content th,
.product-description th {
background: var(--bg-secondary);
color: var(--text-primary);
font-weight: 600;
}
.article-content tr:nth-child(even),
.product-description tr:nth-child(even) {
background: var(--bg-secondary);
}
/* ===== Изображения ===== */
.article-content img,
.product-description img {
max-width: 100%;
height: auto;
margin: 1rem 0;
border-radius: var(--radius-sm, 4px);
}
/* CKEditor 5: figure.image */
.article-content figure.image,
.product-description figure.image {
display: inline-block;
margin: 1em 0;
max-width: 100%;
}
.article-content figure.image img,
.product-description figure.image img {
max-width: 100%;
height: auto;
}
.article-content figure.image figcaption,
.product-description figure.image figcaption {
text-align: center;
font-size: 0.88em;
color: var(--text-light);
margin-top: 0.4em;
}
/* Выравнивание изображений (классы CKEditor 5) */
.article-content .image-style-align-left,
.product-description .image-style-align-left {
float: left;
margin-right: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-right,
.product-description .image-style-align-right {
float: right;
margin-left: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-center,
.product-description .image-style-align-center {
display: block;
margin-left: auto;
margin-right: auto;
max-width: 100%;
}
/* clearfix после float-изображений */
.article-content::after,
.product-description::after {
content: '';
display: table;
clear: both;
}
/* ===== Встроенное видео (iframe/media) ===== */
.article-content .media,
.product-description .media {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
margin: 1.5em 0;
}
.article-content .media iframe,
.product-description .media iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
border-radius: var(--radius-sm, 4px);
}
/* ===== Блоки кода ===== */
.article-content pre,
.product-description pre {
background: #1e1e1e;
color: #d4d4d4;
padding: 1rem 1.25rem;
border-radius: var(--radius-sm, 4px);
overflow-x: auto;
margin: 1rem 0;
font-size: 0.9em;
line-height: 1.5;
}
.article-content code,
.product-description code {
font-family: 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
background: var(--bg-secondary);
color: var(--primary);
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.88em;
}
.article-content pre code,
.product-description pre code {
padding: 0;
background: none;
color: inherit;
}
/* ===== Ссылки внутри контента ===== */
.article-content a,
.product-description a {
color: var(--primary);
text-decoration: underline;
text-underline-offset: 3px;
transition: opacity 0.15s;
}
.article-content a:hover,
.product-description a:hover {
opacity: 0.75;
}
/* ===== Адаптивность ===== */
@media (max-width: 600px) {
.article-content .image-style-align-left,
.article-content .image-style-align-right,
.product-description .image-style-align-left,
.product-description .image-style-align-right {
float: none;
max-width: 100%;
margin: 1em 0;
}
.article-content table,
.product-description table {
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}

View File

@ -0,0 +1,294 @@
/*
* products.css layout и компоненты страниц продуктов.
* Типографика CKEditor-контента вынесена в ck_content.css.
*/
/* ===== Сетка продуктов ===== */
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
/* ===== Карточка продукта ===== */
.product-card {
background: var(--bg-card);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
transition: var(--transition);
overflow: hidden;
display: flex;
flex-direction: column;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.product-image {
width: 100%;
height: 180px;
object-fit: cover;
}
.product-image-placeholder {
width: 100%;
height: 180px;
background: var(--bg-secondary);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-light);
font-size: 2rem;
}
.product-image-placeholder::after {
content: '📦';
}
.product-info {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
}
.product-info h3 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
font-weight: 600;
}
.product-info h3 a {
color: var(--text-primary);
text-decoration: none;
transition: color 0.15s;
}
.product-info h3 a:hover {
color: var(--primary);
}
.product-short {
color: var(--text-secondary);
font-size: 0.9rem;
margin-bottom: 0.75rem;
flex: 1;
line-height: 1.5;
}
.product-meta {
font-size: 0.8rem;
color: var(--text-light);
margin-bottom: 0.75rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.product-category,
.product-configs {
background: var(--bg-secondary);
padding: 0.15rem 0.5rem;
border-radius: var(--radius-sm);
border: 1px solid var(--border-light);
}
/* ===== Цена ===== */
.product-price-block {
margin-top: auto;
}
.product-price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary);
}
.price-restricted {
background: var(--bg-secondary);
padding: 0.6rem;
border-radius: var(--radius-sm);
text-align: center;
}
.price-text {
display: block;
margin-bottom: 0.5rem;
font-size: 0.85rem;
color: var(--text-light);
}
.price-actions {
display: flex;
gap: 0.5rem;
justify-content: center;
}
.btn-small {
padding: 0.4rem 0.8rem;
font-size: 0.85rem;
}
/* ===== Фильтры ===== */
.filters-bar {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1.5rem;
align-items: center;
}
.filter-group {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.filter-group > span {
font-size: 0.85rem;
color: var(--text-light);
font-weight: 500;
}
.filter-btn {
padding: 0.3rem 0.8rem;
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-secondary);
text-decoration: none;
font-size: 0.85rem;
border: 1px solid var(--border-light);
transition: var(--transition);
white-space: nowrap;
}
.filter-btn.active,
.filter-btn:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* ===== Детальная страница ===== */
.product-detail {
max-width: 800px;
margin: 0 auto;
}
.product-header {
margin-bottom: 1.5rem;
}
.product-header h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
color: var(--text-primary);
line-height: 1.2;
}
.product-meta-detail {
display: flex;
gap: 0.75rem;
color: var(--text-light);
font-size: 0.875rem;
flex-wrap: wrap;
}
.product-detail-image {
width: 100%;
max-height: 420px;
object-fit: cover;
border-radius: var(--radius-md);
margin-bottom: 2rem;
}
/* Контент CKEditor — max-width задан на .product-detail, не здесь */
.product-description {
margin-bottom: 2rem;
}
/* ===== Блок цены (детальная) ===== */
.product-price-block-detail {
margin: 2rem 0;
text-align: center;
}
.product-price-large {
font-size: 2rem;
font-weight: 700;
color: var(--primary);
display: block;
margin-bottom: 1rem;
}
.price-restricted-detail {
background: var(--bg-secondary);
padding: 1.5rem;
border-radius: var(--radius-md);
border: 1px solid var(--border-light);
}
.price-restricted-detail p {
margin-bottom: 1rem;
color: var(--text-secondary);
}
.product-back {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border-light);
}
/* ===== Пагинация ===== */
.pagination {
display: flex;
justify-content: center;
gap: 0.4rem;
margin-top: 2.5rem;
flex-wrap: wrap;
}
.pagination a,
.pagination span {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2.2rem;
padding: 0.45rem 0.7rem;
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-secondary);
text-decoration: none;
font-size: 0.9rem;
border: 1px solid var(--border-light);
transition: var(--transition);
}
.pagination a:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.pagination .current {
background: var(--primary);
color: white;
font-weight: 600;
border-color: var(--primary);
}
.pagination .disabled {
opacity: 0.45;
cursor: default;
pointer-events: none;
}
/* ===== Адаптивность ===== */
@media (max-width: 768px) {
.product-header h1 {
font-size: 1.6rem;
}
.product-price-large {
font-size: 1.6rem;
}
}
@media (max-width: 480px) {
.products-grid {
grid-template-columns: 1fr;
}
.price-actions {
flex-direction: column;
align-items: stretch;
}
.filters-bar {
flex-direction: column;
align-items: flex-start;
}
}
.product-card {
cursor: pointer;
}

View File

@ -0,0 +1,10 @@
<h2>🛒 Новый заказ #{{ order.id }}</h2>
<p><strong>Продукт:</strong> {{ order.product.title }}</p>
<p><strong>Конфигурация:</strong> {{ order.configuration.name|default:"Не указана" }}</p>
<p><strong>Клиент:</strong> {{ order.name }}</p>
<p><strong>Телефон:</strong> {{ order.phone }}</p>
<p><strong>Email:</strong> {{ order.email }}</p>
<p><strong>Комментарий:</strong> {{ order.comment|default:"—" }}</p>
<p><strong>Дата создания:</strong> {{ order.created_at|date:"d.m.Y H:i" }}</p>
<hr>
<a href="{{ site_url }}/admin/products/order/{{ order.id }}/change/">Перейти к заказу в админке</a>

View File

@ -0,0 +1,9 @@
<h2>Здравствуйте, {{ order.name }}!</h2>
<p>Ваш заказ <strong>#{{ order.id }}</strong> на продукт <strong>{{ order.product.title }}</strong> успешно принят.</p>
{% if order.configuration %}
<p>Выбранная конфигурация: <strong>{{ order.configuration.name }}</strong></p>
{% endif %}
<p>Мы свяжемся с вами в ближайшее время по телефону <strong>{{ order.phone }}</strong> или эл. почте <strong>{{ order.email }}</strong>, указанных в заказе.</p>
<p>Спасибо за обращение!</p>
<hr>
<small>С.Н.А. Технологии</small>

View File

@ -0,0 +1,17 @@
{% if is_paginated %}
<nav class="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">«</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if num == page_obj.number %}
<span class="current">{{ num }}</span>
{% else %}
<a href="?page={{ num }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">»</a>
{% endif %}
</nav>
{% endif %}

View File

@ -0,0 +1,102 @@
{% extends 'programmer/base.html' %}
{% load static %}
{% load django_bootstrap5 %}
{% block extra_css %}
{# Единый файл для CKEditor-контента (общий с блогом) #}
<link rel="stylesheet" href="{% static 'products/css/ck_content.css' %}">
<link rel="stylesheet" href="{% static 'products/css/products.css' %}">
{% endblock %}
{% block content %}
<div class="product-detail">
{# ===== Шапка ===== #}
<div class="page-header">
<h1 class="page-title">{{ product.title }}</h1>
<div class="product-meta-detail">
<span class="page-subtitle">
Категория:
<a href="{% url 'products:product_list' %}?category={{ product.category.slug }}">
{{ product.category.name }}
</a>
</span>
{% if product.configurations.all %}
<span class="product-configs">{{ product.configurations.all|join:", " }}</span>
{% endif %}
</div>
</div>
<div class="content-card">
{# ===== Изображение ===== #}
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.title }}"
class="product-detail-image" loading="lazy">
{% endif %}
{# ===== CKEditor-контент ===== #}
<div class="product-description">
{{ product.content|safe }}
</div>
{# ===== Цена + действия ===== #}
<div class="product-price-block-detail">
{% if user.is_authenticated %}
<div class="price-authenticated">
<span class="product-price-large">Цена: {{ product.price|floatformat:0 }} ₽</span>
<div class="price-actions">
{# Кнопка для вызова модального окна заказа #}
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#orderModal">
Купить
</button>
</div>
</div>
{% else %}
<div class="price-restricted-detail">
<p>Для просмотра цены и приобретения продукта необходимо авторизоваться.</p>
<div class="price-actions">
<a href="{% url 'login' %}?next={{ request.path }}" class="btn">Войти</a>
</div>
</div>
{% endif %}
</div>
</div>{# /.content-card #}
<div class="product-back">
<a href="{% url 'products:product_list' %}" class="btn btn-outline">К списку продуктов</a>
</div>
</div>{# /.product-detail #}
{# ===== Модальное окно заказа ===== #}
<div class="modal fade" id="orderModal" tabindex="-1" aria-labelledby="orderModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form method="post" action="{% url 'products:order_create' product.slug %}">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" id="orderModalLabel">Оформление заказа: {{ product.title }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
{# Поля формы будут рендериться через переменную order_form #}
{{ order_form.as_p }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="submit" class="btn btn-primary">Отправить заказ</button>
</div>
</form>
</div>
</div>
</div>
<script src="{% static 'programmer/js/floating-button.js' %}"></script>
{% block extra_js %}
{% endblock %}
{% endblock %}

View File

@ -0,0 +1,108 @@
{% extends 'programmer/base.html' %}
{% load static %}
{% load django_bootstrap5 %}
{% block extra_css %}
{# Единый файл для CKEditor-контента (общий с блогом) #}
<link rel="stylesheet" href="{% static 'products/css/ck_content.css' %}">
<link rel="stylesheet" href="{% static 'products/css/products.css' %}">
{% endblock %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Продукты 1С</h1>
<p class="page-subtitle">Готовые модули, расширения и программные продукты для платформы 1С</p>
</div>
{# ===== Фильтры ===== #}
<div class="filters-bar">
<div class="filter-group">
<span>Категория:</span>
<a href="?{% if current_configuration %}configuration={{ current_configuration.slug }}{% endif %}"
class="filter-btn {% if not current_category %}active{% endif %}">Все</a>
{% for cat in categories %}
<a href="?category={{ cat.slug }}{% if current_configuration %}&configuration={{ current_configuration.slug }}{% endif %}"
class="filter-btn {% if current_category == cat %}active{% endif %}">{{ cat.name }}</a>
{% endfor %}
</div>
<div class="filter-group">
<span>Конфигурация:</span>
<a href="?{% if current_category %}category={{ current_category.slug }}{% endif %}"
class="filter-btn {% if not current_configuration %}active{% endif %}">Все</a>
{% for conf in configurations %}
<a href="?configuration={{ conf.slug }}{% if current_category %}&category={{ current_category.slug }}{% endif %}"
class="filter-btn {% if current_configuration == conf %}active{% endif %}">{{ conf.name }}</a>
{% endfor %}
</div>
</div>
{# ===== Сетка продуктов ===== #}
<div class="products-grid">
{% for product in products %}
<div class="product-card" data-url="{{ product.get_absolute_url }}">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.title }}" class="product-image" loading="lazy">
{% else %}
<div class="product-image-placeholder" aria-hidden="true"></div>
{% endif %}
<div class="product-info">
<h3><a href="{{ product.get_absolute_url }}">{{ product.title }}</a></h3>
<p class="product-short">{{ product.short_description|striptags|truncatewords:30 }}</p>
<div class="product-meta">
<span class="product-category">{{ product.category.name }}</span>
{% if product.configurations.all %}
<span class="product-configs">{{ product.configurations.all|join:", " }}</span>
{% endif %}
</div>
<div class="product-price-block">
{% if user.is_authenticated %}
<span class="product-price">{{ product.price|floatformat:0 }} ₽</span>
{% else %}
<div class="price-restricted">
<span class="price-text">Цена доступна после авторизации</span>
<div class="price-actions">
<a href="{% url 'login' %}?next={{ request.path }}" class="btn btn-small">Войти</a>
<button type="button"
onclick="CallbackModal.open('{{ product.title|escapejs }}')"
class="btn btn-small btn-outline">
Запросить цену
</button>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% empty %}
<p class="no-results">Продуктов по выбранным фильтрам не найдено.</p>
{% endfor %}
</div>
<script src="{% static 'programmer/js/floating-button.js' %}"></script>
{% include 'products/includes/pagination.html' %}
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const cards = document.querySelectorAll('.product-card');
cards.forEach(card => {
const url = card.dataset.url;
if (!url) return;
card.addEventListener('click', function(e) {
// Если клик был по ссылке, кнопке или любому элементу с data-no-link
const target = e.target.closest('a, button, .no-link');
if (target) return;
window.location.href = url;
});
});
});
</script>
{% endblock %}

3
products/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
products/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = 'products'
urlpatterns = [
path('', views.ProductListView.as_view(), name='product_list'),
path('<slug:slug>/', views.ProductDetailView.as_view(), name='product_detail'),
path('order/<slug:slug>/', views.order_create, name='order_create'),
]

170
products/views.py Normal file
View File

@ -0,0 +1,170 @@
from django.views.generic import ListView, DetailView
from programmer.mixins import MenuContextMixin, BreadcrumbMixin
from .models import Product, ProductCategory, Configuration
from django.shortcuts import get_object_or_404
from django.http import Http404
from programmer.forms import CallbackForm
from .forms import OrderForm
from django.contrib import messages
from django.shortcuts import redirect
from programmer.utils.email_notifications import send_order_notification, send_order_notification_async
class ProductListView(MenuContextMixin, BreadcrumbMixin, ListView):
model = Product
template_name = 'products/product_list.html'
context_object_name = 'products'
paginate_by = 12
def get_breadcrumbs(self):
return [{'title': 'Продукты', 'url_name': None}]
def get_queryset(self):
qs = (
Product.objects
.filter(is_published=True)
.select_related('category')
.prefetch_related('configurations')
)
# Фильтр по категории
category_slug = self.request.GET.get('category')
if category_slug:
self.current_category = get_object_or_404(ProductCategory, slug=category_slug)
qs = qs.filter(category=self.current_category)
else:
self.current_category = None
# Фильтр по конфигурации
config_slug = self.request.GET.get('configuration')
if config_slug:
self.current_configuration = get_object_or_404(Configuration, slug=config_slug)
qs = qs.filter(configurations=self.current_configuration)
else:
self.current_configuration = None
# ordering уже задан в Meta, явный вызов order_by не нужен,
# но оставляем для явности и возможного расширения сортировки
return qs.order_by('-time_create')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = ProductCategory.objects.all()
context['configurations'] = Configuration.objects.all()
context['current_category'] = self.current_category
context['current_configuration'] = self.current_configuration
context['title'] = "Продукты 1С | Модули и расширения | С.Н.А. Технологии"
context['meta_description'] = (
"Готовые модули, расширения и программные продукты для 1С: Предприятие. "
"Фильтрация по конфигурациям и категориям."
)
context['meta_keywords'] = "1С модули, расширения 1С, программы 1С, купить 1С, продукты 1С"
form = CallbackForm()
if self.request.user.is_authenticated:
if 'captcha' in form.fields:
del form.fields['captcha']
context.update({
'form': form,
})
return context
class ProductDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
model = Product
template_name = 'products/product_detail.html'
context_object_name = 'product'
def get_object(self, queryset=None):
product = super().get_object(queryset=queryset)
if not product.is_published:
raise Http404("Продукт не найден")
return product
def get_breadcrumbs(self):
product = self.object
return [
{'title': 'Продукты', 'url_name': 'products:product_list'},
{
'title': product.category.name,
'url_name': 'products:product_list',
'extra_params': f'?category={product.category.slug}',
},
{'title': product.title, 'url_name': None},
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
product = self.object
context['title'] = product.get_seo_title()
context['meta_description'] = product.get_seo_description()
context['meta_keywords'] = (
f"1С, {product.category.name}, "
f"{', '.join(c.name for c in product.configurations.all())}"
)
# Форма заказа с предзаполнением для авторизованных
initial = {}
if self.request.user.is_authenticated:
user = self.request.user
initial['name'] = user.get_full_name() or user.username
initial['email'] = user.email
# Если есть профиль и телефон
if hasattr(user, 'profile') and user.profile.phone:
initial['phone'] = user.profile.phone
order_form = OrderForm(initial=initial)
order_form.fields['configuration'].queryset = product.configurations.all()
context['order_form'] = order_form
form = CallbackForm()
if self.request.user.is_authenticated:
if 'captcha' in form.fields:
del form.fields['captcha']
context.update({
'form': form,
})
return context
def order_create(request, slug):
product = get_object_or_404(Product, slug=slug, is_published=True)
if request.method == 'POST':
form = OrderForm(request.POST)
# Переопределяем queryset для поля configuration только те, что связаны с продуктом
form.fields['configuration'].queryset = product.configurations.all()
if form.is_valid():
order = form.save(commit=False)
order.product = product
# Если пользователь авторизован, связываем заказ с ним
if request.user.is_authenticated:
order.user = request.user
# Можно предзаполнить имя и email из профиля, если они пустые в форме
if not order.name:
order.name = request.user.get_full_name() or request.user.username
if not order.email:
order.email = request.user.email
order.save()
# Сохраняем выбранную конфигурацию (уже сохранена в форме)
# success = send_order_notification(order)
# if success:
# order.notification_sent = True
# order.save(update_fields=['notification_sent'])
send_order_notification_async(order)
messages.success(request, '✅ Ваш заказ принят! Мы свяжемся с вами в ближайшее время.')
return redirect('products:product_detail', slug=slug)
else:
for field, errors in form.errors.items():
for error in errors:
messages.error(request, f'❌ Ошибка в поле "{field}": {error}')
return redirect('products:product_detail', slug=slug)

View File

@ -1,4 +1,5 @@
from django.conf import settings
from .forms import CallbackForm
def contact_info(request):
@ -6,3 +7,25 @@ def contact_info(request):
'CONTACT_EMAIL': getattr(settings, 'CONTACT_EMAIL', 'it@nserdyuk.ru'),
'CONTACT_PHONE': getattr(settings, 'CONTACT_PHONE', '+7 (960) 469-40-88'),
}
def callback_form(request):
initial = {}
if request.user.is_authenticated:
user = request.user
# Имя: полное имя или логин
initial['name'] = user.get_full_name() or user.username
# Email
initial['email'] = user.email
# Телефон из профиля (если есть)
if hasattr(user, 'profile') and user.profile.phone:
initial['phone'] = user.profile.phone
# Создаём экземпляр формы с initial
form = CallbackForm(initial=initial)
# Для авторизованных пользователей убираем captcha (как у вас в представлениях)
if request.user.is_authenticated and 'captcha' in form.fields:
del form.fields['captcha']
return {'callback_form': form}

View File

@ -30,7 +30,8 @@ class MenuContextMixin(ContextMixin):
'url_name': None,
'children': [
{'title': "Кейсы", 'url_name': 'solution'},
{'title': "Статьи", 'url_name': 'blog'},
{'title': "Статьи", 'url_name': 'blog:article_list'},
{'title': "Продукты", 'url_name': 'products:product_list'},
]
},
{'title': "Отзывы", 'url_name': 'recall'},

View File

@ -196,6 +196,8 @@ class Profile(models.Model):
specialization = models.CharField(max_length=100, blank=True, verbose_name='Специализация')
avatar = models.ImageField(upload_to='avatars/%Y/%m/%d/', blank=True, verbose_name='Аватар')
email_notifications = models.BooleanField(default=True, verbose_name='Получать уведомления')
notify_new_articles = models.BooleanField(default=True, verbose_name="О новых статьях")
notify_new_products = models.BooleanField(default=True, verbose_name="О новых продуктах")
def __str__(self):
return f'Профиль {self.user.username}'

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
select.admin-autocomplete {
width: 20em;
}
@ -274,280 +273,3 @@ select.admin-autocomplete {
display: block;
padding: 6px;
}
=======
select.admin-autocomplete {
width: 20em;
}
.select2-container--admin-autocomplete.select2-container {
min-height: 30px;
}
.select2-container--admin-autocomplete .select2-selection--single,
.select2-container--admin-autocomplete .select2-selection--multiple {
min-height: 30px;
padding: 0;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
border-color: var(--body-quiet-color);
min-height: 30px;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
padding: 0;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
padding: 0;
}
.select2-container--admin-autocomplete .select2-selection--single {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
color: var(--body-fg);
line-height: 30px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
color: var(--body-quiet-color);
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
background-color: var(--darkened-bg);
cursor: default;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none;
}
.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
.select2-container--admin-autocomplete .select2-selection--multiple {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: text;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 10px 5px 5px;
width: 100%;
display: flex;
flex-wrap: wrap;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
list-style: none;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
color: var(--body-quiet-color);
margin-top: 5px;
float: left;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin: 5px;
position: absolute;
right: 0;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
background-color: var(--darkened-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
color: var(--body-quiet-color);
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
color: var(--body-fg);
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
border: solid var(--body-quiet-color) 1px;
outline: 0;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
background-color: var(--darkened-bg);
cursor: default;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
display: none;
}
.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select2-container--admin-autocomplete .select2-search--dropdown {
background: var(--darkened-bg);
}
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
background: var(--body-bg);
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
background: transparent;
color: var(--body-fg);
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield;
}
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
color: var(--body-fg);
background: var(--body-bg);
}
.select2-container--admin-autocomplete .select2-results__option[role=group] {
padding: 0;
}
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
color: var(--body-quiet-color);
}
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
background-color: var(--selected-bg);
color: var(--body-fg);
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
padding-left: 1em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em;
}
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary);
color: var(--primary-fg);
}
.select2-container--admin-autocomplete .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
>>>>>>> master

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* CHANGELISTS */
#changelist {
@ -327,333 +326,3 @@
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}
=======
/* CHANGELISTS */
#changelist {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
#changelist .changelist-form-container {
flex: 1 1 auto;
min-width: 0;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: none;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered .results, .change-list .filtered .paginator,
.filtered #toolbar, .filtered div.xfull {
width: auto;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist-form .results {
overflow-x: auto;
width: 100%;
}
#changelist .toplinks {
border-bottom: 1px solid var(--hairline-color);
}
#changelist .paginator {
color: var(--body-quiet-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--body-bg);
overflow: hidden;
}
/* CHANGELIST TABLES */
#changelist table thead th {
padding: 0;
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td.action-checkbox {
text-align: center;
}
#changelist table tfoot {
color: var(--body-quiet-color);
}
/* TOOLBAR */
#toolbar {
padding: 8px 10px;
margin-bottom: 15px;
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
#toolbar form input {
border-radius: 4px;
font-size: 0.875rem;
padding: 5px;
color: var(--body-fg);
}
#toolbar #searchbar {
height: 1.1875rem;
border: 1px solid var(--border-color);
padding: 2px 5px;
margin: 0;
vertical-align: top;
font-size: 0.8125rem;
max-width: 100%;
}
#toolbar #searchbar:focus {
border-color: var(--body-quiet-color);
}
#toolbar form input[type="submit"] {
border: 1px solid var(--border-color);
font-size: 0.8125rem;
padding: 4px 8px;
margin: 0;
vertical-align: middle;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
color: var(--body-fg);
}
#toolbar form input[type="submit"]:focus,
#toolbar form input[type="submit"]:hover {
border-color: var(--body-quiet-color);
}
#changelist-search img {
vertical-align: middle;
margin-right: 4px;
}
#changelist-search .help {
word-break: break-word;
}
/* FILTER COLUMN */
#changelist-filter {
flex: 0 0 240px;
order: 1;
background: var(--darkened-bg);
border-left: none;
margin: 0 0 0 30px;
}
#changelist-filter h2 {
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 5px 15px;
margin-bottom: 12px;
border-bottom: none;
}
#changelist-filter h3,
#changelist-filter details summary {
font-weight: 400;
padding: 0 15px;
margin-bottom: 10px;
}
#changelist-filter details summary > * {
display: inline;
}
#changelist-filter details > summary {
list-style-type: none;
}
#changelist-filter details > summary::-webkit-details-marker {
display: none;
}
#changelist-filter details > summary::before {
content: '→';
font-weight: bold;
color: var(--link-hover-color);
}
#changelist-filter details[open] > summary::before {
content: '↓';
}
#changelist-filter ul {
margin: 5px 0;
padding: 0 15px 15px;
border-bottom: 1px solid var(--hairline-color);
}
#changelist-filter ul:last-child {
border-bottom: none;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
display: block;
color: var(--body-quiet-color);
word-break: break-word;
}
#changelist-filter li.selected {
border-left: 5px solid var(--hairline-color);
padding-left: 10px;
margin-left: -15px;
}
#changelist-filter li.selected a {
color: var(--link-selected-fg);
}
#changelist-filter a:focus, #changelist-filter a:hover,
#changelist-filter li.selected a:focus,
#changelist-filter li.selected a:hover {
color: var(--link-hover-color);
}
#changelist-filter #changelist-filter-clear a {
font-size: 0.8125rem;
padding-bottom: 10px;
border-bottom: 1px solid var(--hairline-color);
}
/* DATE DRILLDOWN */
.change-list .toplinks {
display: flex;
padding-bottom: 5px;
flex-wrap: wrap;
gap: 3px 17px;
font-weight: bold;
}
.change-list .toplinks a {
font-size: 0.8125rem;
}
.change-list .toplinks .date-back {
color: var(--body-quiet-color);
}
.change-list .toplinks .date-back:focus,
.change-list .toplinks .date-back:hover {
color: var(--link-hover-color);
}
/* ACTIONS */
.filtered .actions {
border-right: none;
}
#changelist table input {
margin: 0;
vertical-align: baseline;
}
/* Once the :has() pseudo-class is supported by all browsers, the tr.selected
selector and the JS adding the class can be removed. */
#changelist tbody tr.selected {
background-color: var(--selected-row);
}
#changelist tbody tr:has(.action-select:checked) {
background-color: var(--selected-row);
}
#changelist .actions {
padding: 10px;
background: var(--body-bg);
border-top: none;
border-bottom: none;
line-height: 1.5rem;
color: var(--body-quiet-color);
width: 100%;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 0.8125rem;
margin: 0 0.5em;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
vertical-align: top;
height: 1.5rem;
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 0.875rem;
padding: 0 0 0 4px;
margin: 0;
margin-left: 10px;
}
#changelist .actions select:focus {
border-color: var(--body-quiet-color);
}
#changelist .actions label {
display: inline-block;
vertical-align: middle;
font-size: 0.8125rem;
}
#changelist .actions .button {
font-size: 0.8125rem;
border: 1px solid var(--border-color);
border-radius: 4px;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
height: 1.5rem;
line-height: 1;
padding: 4px 8px;
margin: 0;
color: var(--body-fg);
}
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
@media (prefers-color-scheme: dark) {
:root {
--primary: #264b5d;
@ -136,142 +135,3 @@ html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
color: var(--body-fg);
background-color: var(--body-bg);
}
=======
@media (prefers-color-scheme: dark) {
:root {
--primary: #264b5d;
--primary-fg: #f7f7f7;
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
--breadcrumbs-bg: var(--primary);
--link-fg: #81d4fa;
--link-hover-color: #4ac1f7;
--link-selected-fg: #6f94c6;
--hairline-color: #272727;
--border-color: #353535;
--error-fg: #e35f5f;
--message-success-bg: #006b1b;
--message-warning-bg: #583305;
--message-error-bg: #570808;
--darkened-bg: #212121;
--selected-bg: #1b1b1b;
--selected-row: #00363a;
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
}
}
html[data-theme="dark"] {
--primary: #264b5d;
--primary-fg: #f7f7f7;
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
--breadcrumbs-bg: var(--primary);
--link-fg: #81d4fa;
--link-hover-color: #4ac1f7;
--link-selected-fg: #6f94c6;
--hairline-color: #272727;
--border-color: #353535;
--error-fg: #e35f5f;
--message-success-bg: #006b1b;
--message-warning-bg: #583305;
--message-error-bg: #570808;
--darkened-bg: #212121;
--selected-bg: #1b1b1b;
--selected-row: #00363a;
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
}
/* THEME SWITCH */
.theme-toggle {
cursor: pointer;
border: none;
padding: 0;
background: transparent;
vertical-align: middle;
margin-inline-start: 5px;
margin-top: -1px;
}
.theme-toggle svg {
vertical-align: middle;
height: 1rem;
width: 1rem;
display: none;
}
/*
Fully hide screen reader text so we only show the one matching the current
theme.
*/
.theme-toggle .visually-hidden {
display: none;
}
html[data-theme="auto"] .theme-toggle .theme-label-when-auto {
display: block;
}
html[data-theme="dark"] .theme-toggle .theme-label-when-dark {
display: block;
}
html[data-theme="light"] .theme-toggle .theme-label-when-light {
display: block;
}
/* ICONS */
.theme-toggle svg.theme-icon-when-auto,
.theme-toggle svg.theme-icon-when-dark,
.theme-toggle svg.theme-icon-when-light {
fill: var(--header-link-color);
color: var(--header-bg);
}
html[data-theme="auto"] .theme-toggle svg.theme-icon-when-auto {
display: block;
}
html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
display: block;
}
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
display: block;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
color: var(--body-fg);
background-color: var(--body-bg);
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
@ -28,34 +27,3 @@ ul.actionlist li {
overflow: hidden;
text-overflow: ellipsis;
}
=======
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
}
.dashboard .module table th {
width: 100%;
}
.dashboard .module table td {
white-space: nowrap;
}
.dashboard .module table td a {
display: block;
padding-right: .6em;
}
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
margin-left: 0;
}
ul.actionlist li {
list-style-type: none;
overflow: hidden;
text-overflow: ellipsis;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
@import url('widgets.css');
/* FORM ROWS */
@ -529,535 +528,3 @@ form .related-widget-wrapper ul {
.clearable-file-input input {
margin-top: 0;
}
=======
@import url('widgets.css');
/* FORM ROWS */
.form-row {
overflow: hidden;
padding: 10px;
font-size: 0.8125rem;
border-bottom: 1px solid var(--hairline-color);
}
.form-row img, .form-row input {
vertical-align: middle;
}
.form-row label input[type="checkbox"] {
margin-top: 0;
vertical-align: 0;
}
form .form-row p {
padding-left: 0;
}
.flex-container {
display: flex;
flex-wrap: wrap;
}
.form-multiline > div {
padding-bottom: 10px;
}
/* FORM LABELS */
label {
font-weight: normal;
color: var(--body-quiet-color);
font-size: 0.8125rem;
}
.required label, label.required {
font-weight: bold;
color: var(--body-fg);
}
/* RADIO BUTTONS */
form div.radiolist div {
padding-right: 7px;
}
form div.radiolist.inline div {
display: inline-block;
}
form div.radiolist label {
width: auto;
}
form div.radiolist input[type="radio"] {
margin: -2px 4px 0 0;
padding: 0;
}
form ul.inline {
margin-left: 0;
padding: 0;
}
form ul.inline li {
float: left;
padding-right: 7px;
}
/* ALIGNED FIELDSETS */
.aligned label {
display: block;
padding: 4px 10px 0 0;
width: 160px;
word-wrap: break-word;
line-height: 1;
}
.aligned label:not(.vCheckboxLabel):after {
content: '';
display: inline-block;
vertical-align: middle;
height: 1.625rem;
}
.aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly {
padding: 6px 0;
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
overflow-wrap: break-word;
}
.aligned ul label {
display: inline;
float: none;
width: auto;
}
.aligned .form-row input {
margin-bottom: 0;
}
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
width: 350px;
}
form .aligned ul {
margin-left: 160px;
padding-left: 10px;
}
form .aligned div.radiolist {
display: inline-block;
margin: 0;
padding: 0;
}
form .aligned p.help,
form .aligned div.help {
margin-top: 0;
margin-left: 160px;
padding-left: 10px;
}
form .aligned p.date div.help.timezonewarning,
form .aligned p.datetime div.help.timezonewarning,
form .aligned p.time div.help.timezonewarning {
margin-left: 0;
padding-left: 0;
font-weight: normal;
}
form .aligned p.help:last-child,
form .aligned div.help:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
form .aligned input + p.help,
form .aligned textarea + p.help,
form .aligned select + p.help,
form .aligned input + div.help,
form .aligned textarea + div.help,
form .aligned select + div.help {
margin-left: 160px;
padding-left: 10px;
}
form .aligned ul li {
list-style: none;
}
form .aligned table p {
margin-left: 0;
padding-left: 0;
}
.aligned .vCheckboxLabel {
float: none;
width: auto;
display: inline-block;
vertical-align: -3px;
padding: 0 0 5px 5px;
}
.aligned .vCheckboxLabel + p.help,
.aligned .vCheckboxLabel + div.help {
margin-top: -4px;
}
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
width: 610px;
}
fieldset .fieldBox {
margin-right: 20px;
}
/* WIDE FIELDSETS */
.wide label {
width: 200px;
}
form .wide p,
form .wide ul.errorlist,
form .wide input + p.help,
form .wide input + div.help {
margin-left: 200px;
}
form .wide p.help,
form .wide div.help {
padding-left: 50px;
}
form div.help ul {
padding-left: 0;
margin-left: 0;
}
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
width: 450px;
}
/* COLLAPSED FIELDSETS */
fieldset.collapsed * {
display: none;
}
fieldset.collapsed h2, fieldset.collapsed {
display: block;
}
fieldset.collapsed {
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
}
fieldset.collapsed h2 {
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
fieldset .collapse-toggle {
color: var(--header-link-color);
}
fieldset.collapsed .collapse-toggle {
background: transparent;
display: inline;
color: var(--link-fg);
}
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: var(--font-family-monospace);
}
/* SUBMIT ROW */
.submit-row {
padding: 12px 14px 12px;
margin: 0 0 20px;
background: var(--darkened-bg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
body.popup .submit-row {
overflow: auto;
}
.submit-row input {
height: 2.1875rem;
line-height: 0.9375rem;
}
.submit-row input, .submit-row a {
margin: 0;
}
.submit-row input.default {
text-transform: uppercase;
}
.submit-row a.deletelink {
margin-left: auto;
}
.submit-row a.deletelink {
display: block;
background: var(--delete-button-bg);
border-radius: 4px;
padding: 0.625rem 0.9375rem;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.closelink {
display: inline-block;
background: var(--close-button-bg);
border-radius: 4px;
padding: 10px 15px;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.deletelink:focus,
.submit-row a.deletelink:hover,
.submit-row a.deletelink:active {
background: var(--delete-button-hover-bg);
text-decoration: none;
}
.submit-row a.closelink:focus,
.submit-row a.closelink:hover,
.submit-row a.closelink:active {
background: var(--close-button-hover-bg);
text-decoration: none;
}
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
vertical-align: top;
}
.vCheckboxField {
border: none;
}
.vDateField, .vTimeField {
margin-right: 2px;
margin-bottom: 4px;
}
.vDateField {
min-width: 6.85em;
}
.vTimeField {
min-width: 4.7em;
}
.vURLField {
width: 30em;
}
.vLargeTextField, .vXMLLargeTextField {
width: 48em;
}
.flatpages-flatpage #id_content {
height: 40.2em;
}
.module table .vPositiveSmallIntegerField {
width: 2.2em;
}
.vIntegerField {
width: 5em;
}
.vBigIntegerField {
width: 10em;
}
.vForeignKeyRawIdAdminField {
width: 5em;
}
.vTextField, .vUUIDField {
width: 20em;
}
/* INLINES */
.inline-group {
padding: 0;
margin: 0 0 30px;
}
.inline-group thead th {
padding: 8px 10px;
}
.inline-group .aligned label {
width: 160px;
}
.inline-related {
position: relative;
}
.inline-related h3 {
margin: 0;
color: var(--body-quiet-color);
padding: 5px;
font-size: 0.8125rem;
background: var(--darkened-bg);
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
}
.inline-related h3 span.delete {
float: right;
}
.inline-related h3 span.delete label {
margin-left: 2px;
font-size: 0.6875rem;
}
.inline-related fieldset {
margin: 0;
background: var(--body-bg);
border: none;
width: 100%;
}
.inline-related fieldset.module h3 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 0.6875rem;
text-align: left;
font-weight: bold;
background: #bcd;
color: var(--body-bg);
}
.inline-group .tabular fieldset.module {
border: none;
}
.inline-related.tabular fieldset.module table {
width: 100%;
overflow-x: scroll;
}
.last-related fieldset {
border: none;
}
.inline-group .tabular tr.has_original td {
padding-top: 2em;
}
.inline-group .tabular tr td.original {
padding: 2px 0 0 0;
width: 0;
_position: relative;
}
.inline-group .tabular th.original {
width: 0px;
padding: 0;
}
.inline-group .tabular td.original p {
position: absolute;
left: 0;
height: 1.1em;
padding: 2px 9px;
overflow: hidden;
font-size: 0.5625rem;
font-weight: bold;
color: var(--body-quiet-color);
_width: 700px;
}
.inline-group ul.tools {
padding: 0;
margin: 0;
list-style: none;
}
.inline-group ul.tools li {
display: inline;
padding: 0 5px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
color: var(--body-quiet-color);
background: var(--darkened-bg);
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group .tabular tr.add-row td {
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group ul.tools a.add,
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
padding-left: 16px;
font-size: 0.75rem;
}
.empty-form {
display: none;
}
/* RELATED FIELD ADD ONE / LOOKUP */
.related-lookup {
margin-left: 5px;
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
background-size: 14px;
}
.related-lookup {
width: 1rem;
height: 1rem;
background-image: url(../img/search.svg);
}
form .related-widget-wrapper ul {
display: inline-block;
margin-left: 0;
padding-left: 0;
}
.clearable-file-input input {
margin-top: 0;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* LOGIN FORM */
.login {
@ -60,66 +59,3 @@
.login .password-reset-link {
text-align: center;
}
=======
/* LOGIN FORM */
.login {
background: var(--darkened-bg);
height: auto;
}
.login #header {
height: auto;
padding: 15px 16px;
justify-content: center;
}
.login #header h1 {
font-size: 1.125rem;
margin: 0;
}
.login #header h1 a {
color: var(--header-link-color);
}
.login #content {
padding: 20px 20px 0;
}
.login #container {
background: var(--body-bg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
width: 28em;
min-width: 300px;
margin: 100px auto;
height: auto;
}
.login .form-row {
padding: 4px 0;
}
.login .form-row label {
display: block;
line-height: 2em;
}
.login .form-row #id_username, .login .form-row #id_password {
padding: 8px;
width: 100%;
box-sizing: border-box;
}
.login .submit-row {
padding: 1em 0 0 0;
margin: 0;
text-align: center;
}
.login .password-reset-link {
text-align: center;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
.sticky {
position: sticky;
top: 0;
@ -143,149 +142,3 @@
#nav-sidebar table {
width: 100%;
}
=======
.sticky {
position: sticky;
top: 0;
max-height: 100vh;
}
.toggle-nav-sidebar {
z-index: 20;
left: 0;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 23px;
width: 23px;
border: 0;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
cursor: pointer;
font-size: 1.25rem;
color: var(--link-fg);
padding: 0;
}
[dir="rtl"] .toggle-nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
}
.toggle-nav-sidebar:hover,
.toggle-nav-sidebar:focus {
background-color: var(--darkened-bg);
}
#nav-sidebar {
z-index: 15;
flex: 0 0 275px;
left: -276px;
margin-left: -276px;
border-top: 1px solid transparent;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
overflow: auto;
}
[dir="rtl"] #nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
left: 0;
margin-left: 0;
right: -276px;
margin-right: -276px;
}
.toggle-nav-sidebar::before {
content: '\00BB';
}
.main.shifted .toggle-nav-sidebar::before {
content: '\00AB';
}
.main > #nav-sidebar {
visibility: hidden;
}
.main.shifted > #nav-sidebar {
margin-left: 0;
visibility: visible;
}
[dir="rtl"] .main.shifted > #nav-sidebar {
margin-right: 0;
}
#nav-sidebar .module th {
width: 100%;
overflow-wrap: anywhere;
}
#nav-sidebar .module th,
#nav-sidebar .module caption {
padding-left: 16px;
}
#nav-sidebar .module td {
white-space: nowrap;
}
[dir="rtl"] #nav-sidebar .module th,
[dir="rtl"] #nav-sidebar .module caption {
padding-left: 8px;
padding-right: 16px;
}
#nav-sidebar .current-app .section:link,
#nav-sidebar .current-app .section:visited {
color: var(--header-color);
font-weight: bold;
}
#nav-sidebar .current-model {
background: var(--selected-row);
}
.main > #nav-sidebar + .content {
max-width: calc(100% - 23px);
}
.main.shifted > #nav-sidebar + .content {
max-width: calc(100% - 299px);
}
@media (max-width: 767px) {
#nav-sidebar, #toggle-nav-sidebar {
display: none;
}
.main > #nav-sidebar + .content,
.main.shifted > #nav-sidebar + .content {
max-width: 100%;
}
}
#nav-filter {
width: 100%;
box-sizing: border-box;
padding: 2px 5px;
margin: 5px 0;
border: 1px solid var(--border-color);
background-color: var(--darkened-bg);
color: var(--body-fg);
}
#nav-filter:focus {
border-color: var(--body-quiet-color);
}
#nav-filter.no-results {
background: var(--message-error-bg);
}
#nav-sidebar table {
width: 100%;
}
>>>>>>> master

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* TABLETS */
@media (max-width: 1024px) {
@ -80,86 +79,3 @@
margin-right: 0;
}
}
=======
/* TABLETS */
@media (max-width: 1024px) {
[dir="rtl"] .colMS {
margin-right: 0;
}
[dir="rtl"] #user-tools {
text-align: right;
}
[dir="rtl"] #changelist .actions label {
padding-left: 10px;
padding-right: 0;
}
[dir="rtl"] #changelist .actions select {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .change-list .filtered .results,
[dir="rtl"] .change-list .filtered .paginator,
[dir="rtl"] .filtered #toolbar,
[dir="rtl"] .filtered div.xfull,
[dir="rtl"] .filtered .actions,
[dir="rtl"] #changelist-filter {
margin-left: 0;
}
[dir="rtl"] .inline-group ul.tools a.add,
[dir="rtl"] .inline-group div.add-row a,
[dir="rtl"] .inline-group .tabular tr.add-row td a {
padding: 8px 26px 8px 10px;
background-position: calc(100% - 8px) 9px;
}
[dir="rtl"] .related-widget-wrapper-link + .selector {
margin-right: 0;
margin-left: 15px;
}
[dir="rtl"] .selector .selector-filter label {
margin-right: 0;
margin-left: 8px;
}
[dir="rtl"] .object-tools li {
float: right;
}
[dir="rtl"] .object-tools li + li {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .dashboard .module table td a {
padding-left: 0;
padding-right: 16px;
}
}
/* MOBILE */
@media (max-width: 767px) {
[dir="rtl"] .aligned .related-lookup,
[dir="rtl"] .aligned .datetimeshortcuts {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .aligned ul,
[dir="rtl"] form .aligned ul.errorlist {
margin-right: 0;
}
[dir="rtl"] #changelist-filter {
margin-left: 0;
margin-right: 0;
}
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* GLOBAL */
th {
@ -287,293 +286,3 @@ form .form-row p.datetime {
margin-left: inherit;
margin-right: 2px;
}
=======
/* GLOBAL */
th {
text-align: right;
}
.module h2, .module caption {
text-align: right;
}
.module ul, .module ol {
margin-left: 0;
margin-right: 1.5em;
}
.viewlink, .addlink, .changelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
.deletelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
.object-tools {
float: left;
}
thead th:first-child,
tfoot td:first-child {
border-left: none;
}
/* LAYOUT */
#user-tools {
right: auto;
left: 0;
text-align: left;
}
div.breadcrumbs {
text-align: right;
}
#content-main {
float: right;
}
#content-related {
float: left;
margin-left: -300px;
margin-right: auto;
}
.colMS {
margin-left: 300px;
margin-right: 0;
}
/* SORTABLE TABLES */
table thead th.sorted .sortoptions {
float: left;
}
thead th.sorted .text {
padding-right: 0;
padding-left: 42px;
}
/* dashboard styles */
.dashboard .module table td a {
padding-left: .6em;
padding-right: 16px;
}
/* changelists styles */
.change-list .filtered table {
border-left: none;
border-right: 0px none;
}
#changelist-filter {
border-left: none;
border-right: none;
margin-left: 0;
margin-right: 30px;
}
#changelist-filter li.selected {
border-left: none;
padding-left: 10px;
margin-left: 0;
border-right: 5px solid var(--hairline-color);
padding-right: 10px;
margin-right: -15px;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-right: none;
border-left: none;
}
/* FORMS */
.aligned label {
padding: 0 0 3px 1em;
}
.submit-row a.deletelink {
margin-left: 0;
margin-right: auto;
}
.vDateField, .vTimeField {
margin-left: 2px;
}
.aligned .form-row input {
margin-left: 5px;
}
form .aligned ul {
margin-right: 163px;
padding-right: 10px;
margin-left: 0;
padding-left: 0;
}
form ul.inline li {
float: right;
padding-right: 0;
padding-left: 7px;
}
form .aligned p.help,
form .aligned div.help {
margin-right: 160px;
padding-right: 10px;
}
form div.help ul,
form .aligned .checkbox-row + .help,
form .aligned p.date div.help.timezonewarning,
form .aligned p.datetime div.help.timezonewarning,
form .aligned p.time div.help.timezonewarning {
margin-right: 0;
padding-right: 0;
}
form .wide p.help, form .wide div.help {
padding-left: 0;
padding-right: 50px;
}
form .wide p,
form .wide ul.errorlist,
form .wide input + p.help,
form .wide input + div.help {
margin-right: 200px;
margin-left: 0px;
}
.submit-row {
text-align: right;
}
fieldset .fieldBox {
margin-left: 20px;
margin-right: 0;
}
.errorlist li {
background-position: 100% 12px;
padding: 0;
}
.errornote {
background-position: 100% 12px;
padding: 10px 12px;
}
/* WIDGETS */
.calendarnav-previous {
top: 0;
left: auto;
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -45px;
}
.calendarnav-next {
top: 0;
right: auto;
left: 10px;
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -15px;
}
.calendar caption, .calendarbox h2 {
text-align: center;
}
.selector {
float: right;
}
.selector .selector-filter {
text-align: right;
}
.selector-add {
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}
.active.selector-add:focus, .active.selector-add:hover {
background-position: 0 -80px;
}
.selector-remove {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}
.active.selector-remove:focus, .active.selector-remove:hover {
background-position: 0 -112px;
}
a.selector-chooseall {
background: url(../img/selector-icons.svg) right -128px no-repeat;
}
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
background-position: 100% -144px;
}
a.selector-clearall {
background: url(../img/selector-icons.svg) 0 -160px no-repeat;
}
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
background-position: 0 -176px;
}
.inline-deletelink {
float: left;
}
form .form-row p.datetime {
overflow: hidden;
}
.related-widget-wrapper {
float: right;
}
/* MISC */
.inline-related h2, .inline-group h2 {
text-align: right
}
.inline-related h3 span.delete {
padding-right: 20px;
padding-left: inherit;
left: 10px;
right: inherit;
float:left;
}
.inline-related h3 span.delete label {
margin-left: inherit;
margin-right: 2px;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
The MIT License (MIT)
Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
@ -20,26 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=======
The MIT License (MIT)
Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
.select2-container {
box-sizing: border-box;
display: inline-block;
@ -480,486 +479,3 @@
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }
=======
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle; }
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-selection--single .select2-selection__clear {
position: relative; }
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px; }
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-search--inline {
float: left; }
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0; }
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051; }
.select2-results {
display: block; }
.select2-results__options {
list-style: none;
margin: 0;
padding: 0; }
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none; }
.select2-results__option[aria-selected] {
cursor: pointer; }
.select2-container--open .select2-dropdown {
left: 0; }
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-search--dropdown {
display: block;
padding: 4px; }
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box; }
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-search--dropdown.select2-search--hide {
display: none; }
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0); }
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important; }
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold; }
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto; }
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none; }
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px;
padding: 1px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0; }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none; }
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa; }
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield; }
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--default .select2-results__option[role=group] {
padding: 0; }
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; }
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white; }
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px; }
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto; }
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0; }
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right;
margin-left: 5px;
margin-right: auto; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0; }
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none; }
.select2-container--classic .select2-dropdown {
background-color: white;
border: 1px solid transparent; }
.select2-container--classic .select2-dropdown--above {
border-bottom: none; }
.select2-container--classic .select2-dropdown--below {
border-top: none; }
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--classic .select2-results__option[role=group] {
padding: 0; }
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey; }
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: white; }
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }
>>>>>>> master

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* SELECTOR (FILTER INTERFACE) */
.selector {
@ -602,608 +601,3 @@ select + .related-widget-wrapper-link,
width: 600px;
height: 400px;
}
=======
/* SELECTOR (FILTER INTERFACE) */
.selector {
width: 800px;
float: left;
display: flex;
}
.selector select {
width: 380px;
height: 17.2em;
flex: 1 0 auto;
}
.selector-available, .selector-chosen {
width: 380px;
text-align: center;
margin-bottom: 5px;
display: flex;
flex-direction: column;
}
.selector-available h2, .selector-chosen h2 {
border: 1px solid var(--border-color);
border-radius: 4px 4px 0 0;
}
.selector-chosen .list-footer-display {
border: 1px solid var(--border-color);
border-top: none;
border-radius: 0 0 4px 4px;
margin: 0 0 10px;
padding: 8px;
text-align: center;
background: var(--primary);
color: var(--header-link-color);
cursor: pointer;
}
.selector-chosen .list-footer-display__clear {
color: var(--breadcrumbs-fg);
}
.selector-chosen h2 {
background: var(--primary);
color: var(--header-link-color);
}
.selector .selector-available h2 {
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
.selector .selector-filter {
border: 1px solid var(--border-color);
border-width: 0 1px;
padding: 8px;
color: var(--body-quiet-color);
font-size: 0.625rem;
margin: 0;
text-align: left;
}
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
float: left;
margin: 7px 0 0;
width: 18px;
height: 18px;
padding: 0;
overflow: hidden;
line-height: 1;
}
.selector .selector-available input,
.selector .selector-chosen input {
width: 320px;
margin-left: 8px;
}
.selector ul.selector-chooser {
align-self: center;
width: 22px;
background-color: var(--selected-bg);
border-radius: 10px;
margin: 0 5px;
padding: 0;
transform: translateY(-17px);
}
.selector-chooser li {
margin: 0;
padding: 3px;
list-style-type: none;
}
.selector select {
padding: 0 10px;
margin: 0 0 10px;
border-radius: 0 0 4px 4px;
}
.selector .selector-chosen--with-filtered select {
margin: 0;
border-radius: 0;
height: 14em;
}
.selector .selector-chosen:not(.selector-chosen--with-filtered) .list-footer-display {
display: none;
}
.selector-add, .selector-remove {
width: 16px;
height: 16px;
display: block;
text-indent: -3000px;
overflow: hidden;
cursor: default;
opacity: 0.55;
}
.active.selector-add, .active.selector-remove {
opacity: 1;
}
.active.selector-add:hover, .active.selector-remove:hover {
cursor: pointer;
}
.selector-add {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}
.active.selector-add:focus, .active.selector-add:hover {
background-position: 0 -112px;
}
.selector-remove {
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}
.active.selector-remove:focus, .active.selector-remove:hover {
background-position: 0 -80px;
}
a.selector-chooseall, a.selector-clearall {
display: inline-block;
height: 16px;
text-align: left;
margin: 1px auto 3px;
overflow: hidden;
font-weight: bold;
line-height: 16px;
color: var(--body-quiet-color);
text-decoration: none;
opacity: 0.55;
}
a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
color: var(--link-fg);
}
a.active.selector-chooseall, a.active.selector-clearall {
opacity: 1;
}
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
cursor: pointer;
}
a.selector-chooseall {
padding: 0 18px 0 0;
background: url(../img/selector-icons.svg) right -160px no-repeat;
cursor: default;
}
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
background-position: 100% -176px;
}
a.selector-clearall {
padding: 0 0 0 18px;
background: url(../img/selector-icons.svg) 0 -128px no-repeat;
cursor: default;
}
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
background-position: 0 -144px;
}
/* STACKED SELECTORS */
.stacked {
float: left;
width: 490px;
display: block;
}
.stacked select {
width: 480px;
height: 10.1em;
}
.stacked .selector-available, .stacked .selector-chosen {
width: 480px;
}
.stacked .selector-available {
margin-bottom: 0;
}
.stacked .selector-available input {
width: 422px;
}
.stacked ul.selector-chooser {
height: 22px;
width: 50px;
margin: 0 0 10px 40%;
background-color: #eee;
border-radius: 10px;
transform: none;
}
.stacked .selector-chooser li {
float: left;
padding: 3px 3px 3px 5px;
}
.stacked .selector-chooseall, .stacked .selector-clearall {
display: none;
}
.stacked .selector-add {
background: url(../img/selector-icons.svg) 0 -32px no-repeat;
cursor: default;
}
.stacked .active.selector-add {
background-position: 0 -32px;
cursor: pointer;
}
.stacked .active.selector-add:focus, .stacked .active.selector-add:hover {
background-position: 0 -48px;
cursor: pointer;
}
.stacked .selector-remove {
background: url(../img/selector-icons.svg) 0 0 no-repeat;
cursor: default;
}
.stacked .active.selector-remove {
background-position: 0 0px;
cursor: pointer;
}
.stacked .active.selector-remove:focus, .stacked .active.selector-remove:hover {
background-position: 0 -16px;
cursor: pointer;
}
.selector .help-icon {
background: url(../img/icon-unknown.svg) 0 0 no-repeat;
display: inline-block;
vertical-align: middle;
margin: -2px 0 0 2px;
width: 13px;
height: 13px;
}
.selector .selector-chosen .help-icon {
background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
}
.selector .search-label-icon {
background: url(../img/search.svg) 0 0 no-repeat;
display: inline-block;
height: 1.125rem;
width: 1.125rem;
}
/* DATE AND TIME */
p.datetime {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-weight: bold;
}
.datetime span {
white-space: nowrap;
font-weight: normal;
font-size: 0.6875rem;
color: var(--body-quiet-color);
}
.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
margin-left: 5px;
margin-bottom: 4px;
}
table p.datetime {
font-size: 0.6875rem;
margin-left: 0;
padding-left: 0;
}
.datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
position: relative;
display: inline-block;
vertical-align: middle;
height: 16px;
width: 16px;
overflow: hidden;
}
.datetimeshortcuts .clock-icon {
background: url(../img/icon-clock.svg) 0 0 no-repeat;
}
.datetimeshortcuts a:focus .clock-icon,
.datetimeshortcuts a:hover .clock-icon {
background-position: 0 -16px;
}
.datetimeshortcuts .date-icon {
background: url(../img/icon-calendar.svg) 0 0 no-repeat;
top: -1px;
}
.datetimeshortcuts a:focus .date-icon,
.datetimeshortcuts a:hover .date-icon {
background-position: 0 -16px;
}
.timezonewarning {
font-size: 0.6875rem;
color: var(--body-quiet-color);
}
/* URL */
p.url {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-size: 0.6875rem;
font-weight: bold;
}
.url a {
font-weight: normal;
}
/* FILE UPLOADS */
p.file-upload {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-size: 0.6875rem;
font-weight: bold;
}
.file-upload a {
font-weight: normal;
}
.file-upload .deletelink {
margin-left: 5px;
}
span.clearable-file-input label {
color: var(--body-fg);
font-size: 0.6875rem;
display: inline;
float: none;
}
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
margin: 5px auto;
font-size: 0.75rem;
width: 19em;
text-align: center;
background: var(--body-bg);
color: var(--body-fg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
}
.clockbox {
width: auto;
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 100%;
}
.calendar caption, .calendarbox h2 {
margin: 0;
text-align: center;
border-top: none;
font-weight: 700;
font-size: 0.75rem;
color: #333;
background: var(--accent);
}
.calendar th {
padding: 8px 5px;
background: var(--darkened-bg);
border-bottom: 1px solid var(--border-color);
font-weight: 400;
font-size: 0.75rem;
text-align: center;
color: var(--body-quiet-color);
}
.calendar td {
font-weight: 400;
font-size: 0.75rem;
text-align: center;
padding: 0;
border-top: 1px solid var(--hairline-color);
border-bottom: none;
}
.calendar td.selected a {
background: var(--primary);
color: var(--button-fg);
}
.calendar td.nonday {
background: var(--darkened-bg);
}
.calendar td.today a {
font-weight: 700;
}
.calendar td a, .timelist a {
display: block;
font-weight: 400;
padding: 6px;
text-decoration: none;
color: var(--body-quiet-color);
}
.calendar td a:focus, .timelist a:focus,
.calendar td a:hover, .timelist a:hover {
background: var(--primary);
color: white;
}
.calendar td a:active, .timelist a:active {
background: var(--header-bg);
color: white;
}
.calendarnav {
font-size: 0.625rem;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link, #calendarnav a:visited,
#calendarnav a:focus, #calendarnav a:hover {
color: var(--body-quiet-color);
}
.calendar-shortcuts {
background: var(--body-bg);
color: var(--body-quiet-color);
font-size: 0.6875rem;
line-height: 0.6875rem;
border-top: 1px solid var(--hairline-color);
padding: 8px 0;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
display: block;
position: absolute;
top: 8px;
width: 15px;
height: 15px;
text-indent: -9999px;
padding: 0;
}
.calendarnav-previous {
left: 10px;
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -15px;
}
.calendarnav-next {
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -45px;
}
.calendar-cancel {
margin: 0;
padding: 4px 0;
font-size: 0.75rem;
background: #eee;
border-top: 1px solid var(--border-color);
color: var(--body-fg);
}
.calendar-cancel:focus, .calendar-cancel:hover {
background: #ddd;
}
.calendar-cancel a {
color: black;
display: block;
}
ul.timelist, .timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}
/* EDIT INLINE */
.inline-deletelink {
float: right;
text-indent: -9999px;
background: url(../img/inline-delete.svg) 0 0 no-repeat;
width: 16px;
height: 16px;
border: 0px none;
}
.inline-deletelink:focus, .inline-deletelink:hover {
cursor: pointer;
}
/* RELATED WIDGET WRAPPER */
.related-widget-wrapper {
float: left; /* display properly in form rows with multiple fields */
overflow: hidden; /* clear floated contents */
}
.related-widget-wrapper-link {
opacity: 0.3;
}
.related-widget-wrapper-link:link {
opacity: .8;
}
.related-widget-wrapper-link:link:focus,
.related-widget-wrapper-link:link:hover {
opacity: 1;
}
select + .related-widget-wrapper-link,
.related-widget-wrapper-link + .related-widget-wrapper-link {
margin-left: 7px;
}
/* GIS MAPS */
.dj_map {
width: 600px;
height: 400px;
}
>>>>>>> master

View File

@ -1,462 +0,0 @@
/*
* article.css
* Styles for CKEditor 5 rendered content inside .content-card
* and for the article detail page layout.
*
* Scope: all rules are under .content-card .card-content
* so they never leak into the rest of the site.
*/
/* ===== ARTICLE PAGE LAYOUT ===== */
.article-meta {
display: flex;
align-items: center;
gap: 1.5rem;
flex-wrap: wrap;
font-size: 0.9rem;
color: var(--text-light);
margin-bottom: 2rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-light);
}
.article-meta span {
display: flex;
align-items: center;
gap: 0.4rem;
}
.article-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1.5rem 0;
}
.article-tag {
display: inline-block;
padding: 0.25rem 0.75rem;
background: var(--bg-tertiary);
border: 1px solid var(--border-light);
border-radius: var(--radius-sm);
font-size: 0.8rem;
color: var(--text-secondary);
text-decoration: none;
transition: var(--transition);
}
.article-tag:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* ===== CKEDITOR 5 CONTENT ===== */
.content-card .card-content {
font-size: 1.05rem;
line-height: 1.8;
color: var(--text-primary);
word-break: break-word;
}
/* --- Headings --- */
.content-card .card-content h2,
.content-card .card-content h3,
.content-card .card-content h4,
.content-card .card-content h5,
.content-card .card-content h6 {
font-weight: 700;
line-height: 1.3;
margin-top: 2rem;
margin-bottom: 0.75rem;
color: var(--text-primary);
}
.content-card .card-content h2 {
font-size: 1.75rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-light);
}
.content-card .card-content h3 {
font-size: 1.375rem;
padding-left: 0.75rem;
border-left: 4px solid var(--primary);
}
.content-card .card-content h4 {
font-size: 1.125rem;
color: var(--text-secondary);
}
/* --- Paragraphs --- */
.content-card .card-content p {
margin-bottom: 1.25rem;
}
.content-card .card-content p:last-child {
margin-bottom: 0;
}
/* --- Links --- */
.content-card .card-content a {
color: var(--primary);
text-decoration: underline;
text-decoration-color: rgba(255, 107, 0, 0.4);
text-underline-offset: 3px;
transition: var(--transition);
}
.content-card .card-content a:hover {
color: var(--primary-dark);
text-decoration-color: var(--primary-dark);
}
/* --- Lists --- */
.content-card .card-content ul,
.content-card .card-content ol {
margin: 0 0 1.25rem 0;
padding-left: 1.75rem;
}
.content-card .card-content ul {
list-style: none;
padding-left: 0;
}
.content-card .card-content ul li {
position: relative;
padding-left: 1.5rem;
margin-bottom: 0.5rem;
}
.content-card .card-content ul li::before {
content: '▸';
position: absolute;
left: 0;
color: var(--primary);
font-weight: bold;
}
.content-card .card-content ol li {
margin-bottom: 0.5rem;
}
.content-card .card-content ul ul,
.content-card .card-content ol ol,
.content-card .card-content ul ol,
.content-card .card-content ol ul {
margin-top: 0.5rem;
margin-bottom: 0;
}
/* --- Blockquote --- */
.content-card .card-content blockquote {
margin: 1.5rem 0;
padding: 1.25rem 1.5rem;
border-left: 4px solid var(--primary);
background: var(--bg-secondary);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
color: var(--text-secondary);
font-style: italic;
font-size: 1.05rem;
}
.content-card .card-content blockquote p:last-child {
margin-bottom: 0;
}
/* --- Inline code (not inside pre) --- */
/*
* High specificity is fine here because this selector explicitly
* excludes pre > code, so it never touches hljs blocks.
*/
.content-card .card-content :not(pre) > code {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.875em;
background: var(--bg-tertiary);
color: var(--primary-dark);
padding: 0.15em 0.4em;
border-radius: 4px;
border: 1px solid var(--border-light);
word-break: break-all;
}
/* --- Code blocks (CKEditor generates <pre><code class="hljs">) --- */
/*
* ALL pre and pre code rules use :where() zero specificity.
* This guarantees hljs always wins on colors, background, and padding.
*
* Responsibility split:
* article.css layout only: margin, border-radius, overflow, shadow
* hljs CSS everything visual: background, color, padding, font-size
*
* font-size:0 on pre prevents inherited line-height:1.8 from inflating
* the spacing between lines (line-height is computed from font-size,
* so zeroing it on the wrapper element neutralises the inheritance).
* font-size is restored on pre code.
*/
:where(.content-card .card-content) pre {
margin: 1.5rem 0;
padding: 0; /* hljs sets padding:1em on code.hljs — don't double it */
border-radius: var(--radius-md);
overflow-x: auto;
box-shadow: var(--shadow-md);
font-size: 0; /* neutralise inherited line-height */
line-height: 1;
background: none; /* hljs controls background (#fff or #282c34) */
}
:where(.content-card .card-content) pre code {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.9rem; /* restore font-size for hljs to work from */
line-height: 1.5;
word-break: normal;
white-space: pre;
/* NO color, NO background, NO padding — hljs owns these */
}
/* --- Tables --- */
.content-card .card-content .table-wrapper,
.content-card .card-content figure.table {
overflow-x: auto;
margin: 1.5rem 0;
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
.content-card .card-content table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
}
.content-card .card-content th {
background: var(--bg-secondary);
font-weight: 700;
text-align: left;
padding: 0.875rem 1rem;
border-bottom: 2px solid var(--border-medium);
color: var(--text-primary);
white-space: nowrap;
}
.content-card .card-content td {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-light);
color: var(--text-secondary);
vertical-align: top;
}
.content-card .card-content tr:last-child td {
border-bottom: none;
}
.content-card .card-content tbody tr:hover td {
background: var(--bg-secondary);
}
/* CKEditor 5 table with header row class */
.content-card .card-content table.ck-table-resized th,
.content-card .card-content table.ck-table-resized td {
border: 1px solid var(--border-light);
}
/* --- Images (CKEditor wraps in <figure class="image">) --- */
.content-card .card-content figure.image {
margin: 1.5rem auto;
text-align: center;
max-width: 100%;
}
.content-card .card-content figure.image img,
.content-card .card-content img {
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
cursor: pointer; /* triggers the click-to-enlarge modal */
}
.content-card .card-content figure.image figcaption {
margin-top: 0.5rem;
font-size: 0.875rem;
color: var(--text-light);
font-style: italic;
text-align: center;
}
/* CKEditor alignment classes */
.content-card .card-content figure.image.image-style-align-left {
float: left;
margin: 0.5rem 1.5rem 1rem 0;
max-width: 45%;
}
.content-card .card-content figure.image.image-style-align-right {
float: right;
margin: 0.5rem 0 1rem 1.5rem;
max-width: 45%;
}
/* Clearfix after floated images */
.content-card .card-content::after {
content: '';
display: table;
clear: both;
}
/* --- Horizontal rule --- */
.content-card .card-content hr {
border: none;
height: 2px;
background: linear-gradient(90deg, transparent, var(--border-medium), transparent);
margin: 2rem 0;
}
/* --- Alert/info boxes (CKEditor custom classes if used) --- */
.content-card .card-content .info-box {
padding: 1rem 1.25rem;
border-radius: var(--radius-md);
margin: 1.5rem 0;
border-left: 4px solid var(--accent);
background: rgba(0, 168, 255, 0.08);
color: var(--text-secondary);
font-size: 0.95rem;
}
/* ===== COMMENTS SECTION ===== */
.comments-section {
margin-top: 3rem;
}
.comments-section h3 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.75rem;
}
.comment-card {
background: var(--bg-secondary);
border-radius: var(--radius-lg);
padding: 1.25rem 1.5rem;
margin-bottom: 1rem;
border: 1px solid var(--border-light);
border-left: 3px solid var(--secondary);
}
.comment-author {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
}
.comment-date {
font-size: 0.8rem;
color: var(--text-light);
margin-left: 0.75rem;
}
.comment-body {
margin-top: 0.75rem;
color: var(--text-secondary);
line-height: 1.6;
}
.comment-form-card {
background: var(--bg-card);
border-radius: var(--radius-xl);
padding: 2rem;
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
margin-top: 2rem;
position: relative;
}
.comment-form-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--gradient-secondary);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.comment-form-card h4 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: var(--text-primary);
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.content-card .card-content h2 {
font-size: 1.5rem;
}
.content-card .card-content h3 {
font-size: 1.25rem;
}
.content-card .card-content figure.image.image-style-align-left,
.content-card .card-content figure.image.image-style-align-right {
float: none;
max-width: 100%;
margin: 1rem auto;
}
/* :where() keeps specificity at zero so hljs still wins on mobile */
:where(.content-card .card-content) pre {
box-shadow: none;
margin: 1rem 0;
}
.content-card .card-content th,
.content-card .card-content td {
padding: 0.6rem 0.75rem;
font-size: 0.875rem;
}
.article-meta {
gap: 1rem;
font-size: 0.8rem;
}
}
@media (max-width: 480px) {
.content-card .card-content {
font-size: 1rem;
}
.content-card .card-content h2 {
font-size: 1.375rem;
}
.content-card .card-content h3 {
font-size: 1.125rem;
}
}

View File

@ -1,139 +0,0 @@
/* Основная типографика */
.article-content {
font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px; /* при необходимости */
margin: 0 auto;
}
.article-content h1,
.article-content h2,
.article-content h3,
.article-content h4,
.article-content h5,
.article-content h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
font-weight: 600;
line-height: 1.25;
}
.article-content p {
margin-bottom: 1.2em;
}
.article-content blockquote {
border-left: 4px solid #ddd;
padding-left: 1em;
margin-left: 0;
margin-right: 0;
font-style: italic;
color: #666;
}
.article-content pre {
background: #f5f5f5;
padding: 1em;
border-radius: 4px;
overflow-x: auto;
}
.article-content code {
background: #f0f0f0;
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 0.9em;
}
/* Таблицы */
.article-content table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
}
.article-content th,
.article-content td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.article-content th {
background-color: #f8f8f8;
}
/* Изображения */
.article-content figure.image {
display: inline-block;
margin: 1em 0;
max-width: 100%;
}
.article-content figure.image img {
max-width: 100%;
height: auto;
}
/* Выравнивание изображений (классы, используемые CKEditor 5) */
.article-content .image-style-align-left {
float: left;
margin-right: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-right {
float: right;
margin-left: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-center {
display: block;
margin-left: auto;
margin-right: auto;
max-width: 100%;
}
/* Подписи к изображениям (если используются) */
.article-content figure.image figcaption {
text-align: center;
font-size: 0.9em;
color: #666;
margin-top: 0.3em;
}
/* Встроенные медиа (видео) */
.article-content .media {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
margin: 1.5em 0;
}
.article-content .media iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
/* Списки */
.article-content ul,
.article-content ol {
padding-left: 2em;
margin-bottom: 1em;
}
/* Горизонтальная линия */
.article-content hr {
border: none;
border-top: 2px solid #eee;
margin: 2em 0;
}

View File

@ -914,6 +914,14 @@ body {
margin: 0 auto;
}
.product-configs {
color: var(--text-secondary);
font-size: 1.25rem;
font-weight: 500;
max-width: 600px;
margin: 0 auto;
}
/* ===== FOOTER ===== */
.footer {
background: var(--footer-bg);
@ -1665,6 +1673,11 @@ body {
padding: 0 1rem;
}
.product-configs {
font-size: 1.125rem;
padding: 0 1rem;
}
.grid-2 {
grid-template-columns: 1fr;
gap: 1.5rem;

View File

@ -16,9 +16,17 @@ const CallbackModal = {
}, 5000);
},
open() {
open(productTitle) {
const modal = document.getElementById(this.modalId);
if (modal) {
if (productTitle) {
const questionField = modal.querySelector('#id_question');
if (questionField) {
questionField.value = `Запрос цены по ПП "${productTitle}"`;
}
}
modal.style.display = 'block';
const floatingBtn = document.getElementById(this.floatingBtnId);
if (floatingBtn && this.shown) {

View File

@ -0,0 +1,30 @@
// programmer/static/programmer/js/profile.js
function openModalContent(el) {
const url = el.dataset.url;
const title = el.querySelector('h4').innerText;
const modal = document.getElementById('contentModal');
const body = document.getElementById('contentModalBody');
const titleEl = document.getElementById('contentModalTitle');
titleEl.textContent = title;
body.innerHTML = '<p>Загрузка...</p>';
modal.style.display = 'block';
fetch(url)
.then(response => response.text())
.then(html => {
body.innerHTML = html;
})
.catch(() => {
body.innerHTML = '<p style="color:red;">Ошибка загрузки. Попробуйте позже.</p>';
});
}
function closeContentModal() {
document.getElementById('contentModal').style.display = 'none';
}
// Закрытие по клику на фон и ESC
document.addEventListener('click', function(e) {
if (e.target === document.getElementById('contentModal')) closeContentModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeContentModal();
});

View File

@ -393,30 +393,32 @@
<form method="post" action="{% url 'callback' %}" id="callbackForm">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<div class="form-group">
<label for="id_name">Имя *</label>
{{ form.name }}
{{ callback_form.name }}
</div>
<div class="form-group">
<label for="id_phone">Телефон *</label>
{{ form.phone }}
{{ callback_form.phone }}
</div>
<div class="form-group">
<label for="id_email">Электронная почта</label>
{{ form.email }}
{{ callback_form.email }}
</div>
<div class="form-group">
<label for="id_question">Ваш вопрос</label>
{{ form.question }}
{{ callback_form.question }}
</div>
{% if form.captcha %}
<div class="form-group">
<label for="id_captcha">Защитный код *</label>
{{ form.captcha }}
{{ callback_form.captcha }}
</div>
{% endif %}
@ -430,6 +432,19 @@
</div>
</div>
<!-- Универсальное модальное окно -->
<div id="contentModal" class="modal">
<div class="modal-content" style="max-width: 700px;">
<div class="modal-header">
<h3 id="contentModalTitle">Заголовок</h3>
<button class="modal-close" onclick="closeContentModal()">&times;</button>
</div>
<div class="modal-body" id="contentModalBody">
<!-- сюда подгружается содержимое -->
</div>
</div>
</div>
<!-- Плавающая кнопка -->
<div id="floatingButton" class="floating-btn" style="position: fixed; bottom: 30px; right: 30px; z-index: 10000; display: none;">
<button onclick="CallbackModal.open()" class="btn btn-primary pulse">🎯 Получить консультацию</button>

View File

@ -0,0 +1,80 @@
<style>
.status-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
margin-right: 0.5rem;
}
.status-badge.new { background: #dc3545; color: #fff; }
.status-badge.read { background: #28a745; color: #fff; }
.status-badge.processed { background: #17a2b8; color: #fff; }
.status-badge.unprocessed { background: #ffc107; color: #212529; }
.status-badge.sent { background: #6c757d; color: #fff; }
.list-item {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border-light);
}
.list-item:last-child { border-bottom: none; }
.list-item .meta { font-size: 0.9rem; color: var(--text-light); }
</style>
<h3>📞 Мои заявки на звонок</h3>
{% if callbacks %}
{% for cb in callbacks %}
<div class="list-item">
<div>
<strong>{{ cb.name }}</strong>
<span class="status-badge {% if cb.is_read %}read{% else %}new{% endif %}">
{% if cb.is_read %}✅ Прочитано{% else %}🆕 Новое{% endif %}
</span>
<span class="status-badge {% if cb.is_processed %}processed{% else %}unprocessed{% endif %}">
{% if cb.is_processed %}✅ Обработано{% else %}⏳ В обработке{% endif %}
</span>
</div>
<div class="meta">
{{ cb.time_create|date:"d.m.Y H:i" }} •
📞 {{ cb.phone }} •
✉️ {{ cb.email|default:"email не указан" }}
{% if cb.question %}
<br><span style="font-style:italic;">{{ cb.question|truncatechars:80 }}</span>
{% endif %}
</div>
</div>
{% endfor %}
{% else %}
<p>Заявок пока нет.</p>
{% endif %}
<hr>
<h3>🛒 Мои заказы</h3>
{% if orders %}
{% for order in orders %}
<div class="list-item">
<div>
<strong>{{ order.product.title }}</strong>
<span class="status-badge {% if order.is_processed %}processed{% else %}unprocessed{% endif %}">
{% if order.is_processed %}✅ Обработан{% else %}⏳ В обработке{% endif %}
</span>
{% if order.notification_sent %}
<span class="status-badge sent">📨 Уведомление отправлено</span>
{% endif %}
</div>
<div class="meta">
{{ order.created_at|date:"d.m.Y H:i" }} •
📞 {{ order.phone }} •
✉️ {{ order.email }}
{% if order.configuration %}
<br>⚙️ Конфигурация: {{ order.configuration.name }}
{% endif %}
{% if order.comment %}
<br><span style="font-style:italic;">{{ order.comment|truncatechars:80 }}</span>
{% endif %}
</div>
</div>
{% endfor %}
{% else %}
<p>Заказов пока нет.</p>
{% endif %}

View File

@ -0,0 +1,59 @@
<style>
.comment-status {
display: inline-block;
padding: 0.2rem 0.6rem;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 600;
margin-left: 0.5rem;
}
.comment-status.moderation {
background: #ffc107;
color: #212529;
}
.comment-status.approved {
background: #28a745;
color: #fff;
}
.list-item {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border-light);
}
.list-item:last-child { border-bottom: none; }
.list-item .meta { font-size: 0.9rem; color: var(--text-light); }
.list-item .article-link {
font-weight: 600;
color: var(--primary);
text-decoration: none;
transition: color 0.2s;
}
.list-item .article-link:hover {
color: var(--primary-dark);
text-decoration: underline;
}
</style>
<h3>💬 Мои комментарии</h3>
{% if comments %}
{% for comment in comments %}
<div class="list-item">
<div>
<a href="{{ comment.article.get_absolute_url }}" class="article-link">
{{ comment.article.title }}
</a>
{% if not comment.is_moderated %}
<span class="comment-status moderation">На модерации</span>
{% else %}
<span class="comment-status approved">✅ Одобрен</span>
{% endif %}
</div>
<div class="meta">
{{ comment.time_create|date:"d.m.Y H:i" }}
<br>
{{ comment.content|truncatechars:150 }}
</div>
</div>
{% endfor %}
{% else %}
<p>Вы ещё не оставили ни одного комментария.</p>
{% endif %}

View File

@ -0,0 +1,25 @@
<h3>🔔 Настройки уведомлений</h3>
<form id="notif-form">
{% csrf_token %}
<label>
<input type="checkbox" name="notify_new_articles" {% if profile.notify_new_articles %}checked{% endif %}>
Уведомлять о новых статьях
</label><br>
<label>
<input type="checkbox" name="notify_new_products" {% if profile.notify_new_products %}checked{% endif %}>
Уведомлять о новых продуктах
</label><br>
<button type="submit" class="btn btn-primary">Сохранить</button>
</form>
<script>
document.getElementById('notif-form').addEventListener('submit', function(e) {
e.preventDefault();
fetch('{% url "notification_settings" %}', {
method: 'POST',
body: new FormData(this),
headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value }
})
.then(response => response.json())
.then(data => { if (data.status === 'ok') alert('Настройки сохранены!'); });
});
</script>

View File

@ -1,5 +1,6 @@
{% extends 'programmer/base.html' %}
{% load django_bootstrap5 %}
{% load static %}
{% block content %}
<div class="page-header">
@ -31,21 +32,22 @@
</div>
</div>
<div class="content-card mt-3">
<h3>Мои действия</h3>
<div class="skills-grid">
<div class="skill-category">
<div class="skills-grid">
<div class="skill-category" data-url="{% url 'my_callbacks' %}" onclick="openModalContent(this)">
<h4>📋 Мои заявки</h4>
<p>Просмотр истории обращений</p>
</div>
<div class="skill-category">
<div class="skill-category" data-url="{% url 'my_comments' %}" onclick="openModalContent(this)">
<h4>💬 Мои комментарии</h4>
<p>Управление комментариями к статьям</p>
</div>
<div class="skill-category">
<div class="skill-category" data-url="{% url 'notification_settings' %}" onclick="openModalContent(this)">
<h4>🔔 Настройки уведомлений</h4>
<p>Email-рассылка и оповещения</p>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="{% static 'programmer/js/profile.js' %}"></script>
{% endblock %}

View File

@ -35,6 +35,9 @@ urlpatterns = [
path('privacy/', PrivacyPolicyView.as_view(), name='privacy'),
path('yandex_cdc16c33291495b9.html/', yandex_html, name='yandex_cdc16c33291495b9'),
path('requisites/', RequisitesPageView.as_view(), name='requisites'),
path('profile/callbacks/', views.my_callbacks, name='my_callbacks'),
path('profile/comments/', views.my_comments, name='my_comments'),
path('profile/notifications/', views.notification_settings, name='notification_settings'),
]

View File

@ -104,3 +104,191 @@ def send_multiple_callback_notifications(callbacks):
if connection:
connection.close()
return 0
# ===== Уведомления о заказах =====
def send_order_notification(order):
"""
Отправляет уведомление о новом заказе:
- администратору (на ADMIN_EMAILS)
- клиенту (на email из заказа, если указан)
Возвращает True, если хотя бы одно письмо отправлено.
"""
try:
if not all([settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD]):
logger.error("Email settings are not configured properly")
return False
admin_sent = False
client_sent = False
# Письмо администратору
subject_admin = f'🛒 Новый заказ #{order.id} от {order.name}'
html_admin = render_to_string('products/emails/order_admin.html', {
'order': order,
'product': order.product,
'configuration': order.configuration,
'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost',
})
plain_admin = strip_tags(html_admin)
send_mail(
subject=subject_admin,
message=plain_admin,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=settings.ADMIN_EMAILS,
html_message=html_admin,
fail_silently=False,
)
admin_sent = True
logger.info(f"Order admin notification sent for order #{order.id}")
# Письмо клиенту (если email указан)
if order.email:
subject_client = f'✅ Ваш заказ #{order.id} на {order.product.title} принят'
html_client = render_to_string('products/emails/order_client.html', {
'order': order,
'product': order.product,
'configuration': order.configuration,
'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost',
})
plain_client = strip_tags(html_client)
send_mail(
subject=subject_client,
message=plain_client,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[order.email],
html_message=html_client,
fail_silently=False,
)
client_sent = True
logger.info(f"Order client notification sent to {order.email} for order #{order.id}")
return admin_sent or client_sent
except Exception as e:
logger.error(f"Error sending order notification for order #{order.id}: {e}")
return False
def send_multiple_order_notifications(orders):
"""
Отправляет уведомления для нескольких заказов, используя одно SMTP-соединение.
Возвращает количество успешно отправленных заказов.
"""
if not orders:
return 0
connection = get_connection()
try:
connection.open()
count = 0
for order in orders:
try:
# Администратору
subject_admin = f'🛒 Новый заказ #{order.id} от {order.name}'
html_admin = render_to_string('products/emails/order_admin.html', {
'order': order,
'product': order.product,
'configuration': order.configuration,
'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost',
})
plain_admin = strip_tags(html_admin)
send_mail(
subject=subject_admin,
message=plain_admin,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=settings.ADMIN_EMAILS,
html_message=html_admin,
connection=connection,
fail_silently=False,
)
# Клиенту
if order.email:
subject_client = f'✅ Ваш заказ #{order.id} на {order.product.title} принят'
html_client = render_to_string('products/emails/order_client.html', {
'order': order,
'product': order.product,
'configuration': order.configuration,
'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost',
})
plain_client = strip_tags(html_client)
send_mail(
subject=subject_client,
message=plain_client,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[order.email],
html_message=html_client,
connection=connection,
fail_silently=False,
)
count += 1
logger.info(f"Order notifications sent for order #{order.id}")
except Exception as e:
logger.error(f"Error sending order notifications for order #{order.id}: {e}")
connection.close()
return count
except Exception as e:
logger.error(f"Connection error in send_multiple_order_notifications: {e}")
if connection:
connection.close()
return 0
def send_test_order_email():
"""Тестовая отправка письма для проверки настроек почты."""
try:
send_mail(
subject='📧 Test Order Email',
message='This is a test email for order notifications.',
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=settings.ADMIN_EMAILS,
fail_silently=False,
)
return True
except Exception as e:
logger.error(f"Test order email failed: {e}")
return False
# Асинхронная отправка
def send_order_notification_async(order):
"""Выбирает способ отправки в зависимости от DEBUG."""
if settings.DEBUG:
# Используем background-tasks
from .tasks import send_order_notification_background
send_order_notification_background(order.id)
return True # задача поставлена в очередь
else:
# Используем Celery
try:
from .tasks import send_order_notification_celery
send_order_notification_celery.delay(order.id)
return True
except (ImportError, AttributeError):
# fallback на синхронную отправку
from .email_notifications import send_order_notification
return send_order_notification(order)
def send_multiple_order_notifications_async(orders):
if settings.DEBUG:
from .tasks import send_multiple_order_notifications_background
order_ids = list(orders.values_list('id', flat=True))
send_multiple_order_notifications_background(order_ids)
return len(order_ids)
else:
try:
from .tasks import send_multiple_order_notifications_celery
order_ids = list(orders.values_list('id', flat=True))
send_multiple_order_notifications_celery.delay(order_ids)
return len(order_ids)
except (ImportError, AttributeError):
from .email_notifications import send_multiple_order_notifications
return send_multiple_order_notifications(orders)

56
programmer/utils/tasks.py Normal file
View File

@ -0,0 +1,56 @@
from background_task import background
from django.conf import settings
import logging
logger = logging.getLogger(__name__)
@background(schedule=0)
def send_order_notification_background(order_id):
"""Асинхронная отправка уведомлений о заказе через background-tasks."""
from .email_notifications import send_order_notification
from products.models import Order
try:
order = Order.objects.select_related('product', 'configuration').get(id=order_id)
success = send_order_notification(order)
if success:
order.notification_sent = True
order.save(update_fields=['notification_sent'])
return success
except Order.DoesNotExist:
logger.error(f"Order #{order_id} not found")
return False
@background(schedule=0)
def send_multiple_order_notifications_background(order_ids):
"""Отправка уведомлений для нескольких заказов."""
from .email_notifications import send_multiple_order_notifications
from products.models import Order
orders = Order.objects.filter(id__in=order_ids)
return send_multiple_order_notifications(orders)
try:
from celery import shared_task
except ImportError:
shared_task = None
if shared_task is not None:
@shared_task
def send_order_notification_celery(order_id):
from .email_notifications import send_order_notification
from products.models import Order
try:
order = Order.objects.select_related('product', 'configuration').get(id=order_id)
success = send_order_notification(order)
if success:
order.notification_sent = True
order.save(update_fields=['notification_sent'])
return success
except Order.DoesNotExist:
return False
@shared_task
def send_multiple_order_notifications_celery(order_ids):
from .email_notifications import send_multiple_order_notifications
from products.models import Order
orders = Order.objects.filter(id__in=order_ids)
return send_multiple_order_notifications(orders)

View File

@ -10,6 +10,7 @@ from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import CallbackForm, ProfileForm, UserEditForm, RegistrationForm
from django.utils import timezone
from django.urls import reverse
from datetime import timedelta
from django.db.models import Count, QuerySet
from django.contrib.auth.decorators import login_required, user_passes_test
@ -19,6 +20,9 @@ from .mixins import PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin
from .services import get_published_queryset, track_page_view
from typing import Any, Dict, Type
from django.template.loader import render_to_string
from django.utils.http import url_has_allowed_host_and_scheme
from blog.models import Comment
from products.models import Order
import os
@ -392,6 +396,10 @@ def callback_request(request: HttpRequest) -> HttpResponse:
if 'captcha' in form.fields:
del form.fields['captcha']
next_url = request.POST.get('next', '')
if not next_url or not url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}):
next_url = reverse('home')
if form.is_valid():
# Сохраняем заявку
callback = form.save()
@ -402,7 +410,7 @@ def callback_request(request: HttpRequest) -> HttpResponse:
messages.success(
request,
'✅ Ваша заявка успешно отправлена! Я свяжусь с вами в ближайшее время.'
'✅ Ваша заявка успешно отправлена! Мы свяжемся с вами в ближайшее время.'
)
else:
# Собираем все ошибки формы
@ -414,7 +422,7 @@ def callback_request(request: HttpRequest) -> HttpResponse:
f'❌ Ошибка в поле "{field_label}": {error}'
)
return redirect('home')
return redirect(next_url)
@ -478,3 +486,29 @@ def robots_txt(request):
def yandex_html(request: HttpRequest) -> HttpResponse:
"""Отдает yandex_cdc16c33291495b9.html."""
return render(request, 'programmer/yandex_cdc16c33291495b9.html', content_type='text/html')
@login_required
def my_callbacks(request):
callbacks = CallbackRequest.objects.filter(email=request.user.email).order_by('-time_create')
# Можно также фильтровать по телефону, если он заполнен в профиле, но email — универсальнее.
orders = Order.objects.filter(email=request.user.email).order_by('-created_at') # если поле есть
return render(request, 'programmer/includes/my_callbacks.html', {
'callbacks': callbacks,
'orders': orders,
})
@login_required
def my_comments(request):
comments = Comment.objects.filter(user=request.user).select_related('article').order_by('-time_create')
return render(request, 'programmer/includes/my_comments.html', {'comments': comments})
@login_required
def notification_settings(request):
profile = request.user.profile
if request.method == 'POST':
# обработка формы (можно через AJAX POST)
profile.notify_new_articles = request.POST.get('notify_new_articles') == 'on'
profile.notify_new_products = request.POST.get('notify_new_products') == 'on'
profile.save()
return JsonResponse({'status': 'ok'})
return render(request, 'programmer/includes/notification_settings.html', {'profile': profile})

View File

@ -11,3 +11,4 @@ django_ckeditor_5
django-allauth
django-simple-captcha
django-turnstile
celery

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
select.admin-autocomplete {
width: 20em;
}
@ -274,280 +273,3 @@ select.admin-autocomplete {
display: block;
padding: 6px;
}
=======
select.admin-autocomplete {
width: 20em;
}
.select2-container--admin-autocomplete.select2-container {
min-height: 30px;
}
.select2-container--admin-autocomplete .select2-selection--single,
.select2-container--admin-autocomplete .select2-selection--multiple {
min-height: 30px;
padding: 0;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
border-color: var(--body-quiet-color);
min-height: 30px;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
padding: 0;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
padding: 0;
}
.select2-container--admin-autocomplete .select2-selection--single {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
color: var(--body-fg);
line-height: 30px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
color: var(--body-quiet-color);
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
background-color: var(--darkened-bg);
cursor: default;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none;
}
.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
.select2-container--admin-autocomplete .select2-selection--multiple {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: text;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 10px 5px 5px;
width: 100%;
display: flex;
flex-wrap: wrap;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
list-style: none;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
color: var(--body-quiet-color);
margin-top: 5px;
float: left;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin: 5px;
position: absolute;
right: 0;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
background-color: var(--darkened-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
color: var(--body-quiet-color);
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px;
}
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
color: var(--body-fg);
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
border: solid var(--body-quiet-color) 1px;
outline: 0;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
background-color: var(--darkened-bg);
cursor: default;
}
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
display: none;
}
.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select2-container--admin-autocomplete .select2-search--dropdown {
background: var(--darkened-bg);
}
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
background: var(--body-bg);
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
background: transparent;
color: var(--body-fg);
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield;
}
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
color: var(--body-fg);
background: var(--body-bg);
}
.select2-container--admin-autocomplete .select2-results__option[role=group] {
padding: 0;
}
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
color: var(--body-quiet-color);
}
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
background-color: var(--selected-bg);
color: var(--body-fg);
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
padding-left: 1em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em;
}
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em;
}
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary);
color: var(--primary-fg);
}
.select2-container--admin-autocomplete .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
>>>>>>> master

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* CHANGELISTS */
#changelist {
@ -327,333 +326,3 @@
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}
=======
/* CHANGELISTS */
#changelist {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
#changelist .changelist-form-container {
flex: 1 1 auto;
min-width: 0;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: none;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered .results, .change-list .filtered .paginator,
.filtered #toolbar, .filtered div.xfull {
width: auto;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist-form .results {
overflow-x: auto;
width: 100%;
}
#changelist .toplinks {
border-bottom: 1px solid var(--hairline-color);
}
#changelist .paginator {
color: var(--body-quiet-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--body-bg);
overflow: hidden;
}
/* CHANGELIST TABLES */
#changelist table thead th {
padding: 0;
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td.action-checkbox {
text-align: center;
}
#changelist table tfoot {
color: var(--body-quiet-color);
}
/* TOOLBAR */
#toolbar {
padding: 8px 10px;
margin-bottom: 15px;
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
#toolbar form input {
border-radius: 4px;
font-size: 0.875rem;
padding: 5px;
color: var(--body-fg);
}
#toolbar #searchbar {
height: 1.1875rem;
border: 1px solid var(--border-color);
padding: 2px 5px;
margin: 0;
vertical-align: top;
font-size: 0.8125rem;
max-width: 100%;
}
#toolbar #searchbar:focus {
border-color: var(--body-quiet-color);
}
#toolbar form input[type="submit"] {
border: 1px solid var(--border-color);
font-size: 0.8125rem;
padding: 4px 8px;
margin: 0;
vertical-align: middle;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
color: var(--body-fg);
}
#toolbar form input[type="submit"]:focus,
#toolbar form input[type="submit"]:hover {
border-color: var(--body-quiet-color);
}
#changelist-search img {
vertical-align: middle;
margin-right: 4px;
}
#changelist-search .help {
word-break: break-word;
}
/* FILTER COLUMN */
#changelist-filter {
flex: 0 0 240px;
order: 1;
background: var(--darkened-bg);
border-left: none;
margin: 0 0 0 30px;
}
#changelist-filter h2 {
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 5px 15px;
margin-bottom: 12px;
border-bottom: none;
}
#changelist-filter h3,
#changelist-filter details summary {
font-weight: 400;
padding: 0 15px;
margin-bottom: 10px;
}
#changelist-filter details summary > * {
display: inline;
}
#changelist-filter details > summary {
list-style-type: none;
}
#changelist-filter details > summary::-webkit-details-marker {
display: none;
}
#changelist-filter details > summary::before {
content: '→';
font-weight: bold;
color: var(--link-hover-color);
}
#changelist-filter details[open] > summary::before {
content: '↓';
}
#changelist-filter ul {
margin: 5px 0;
padding: 0 15px 15px;
border-bottom: 1px solid var(--hairline-color);
}
#changelist-filter ul:last-child {
border-bottom: none;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
display: block;
color: var(--body-quiet-color);
word-break: break-word;
}
#changelist-filter li.selected {
border-left: 5px solid var(--hairline-color);
padding-left: 10px;
margin-left: -15px;
}
#changelist-filter li.selected a {
color: var(--link-selected-fg);
}
#changelist-filter a:focus, #changelist-filter a:hover,
#changelist-filter li.selected a:focus,
#changelist-filter li.selected a:hover {
color: var(--link-hover-color);
}
#changelist-filter #changelist-filter-clear a {
font-size: 0.8125rem;
padding-bottom: 10px;
border-bottom: 1px solid var(--hairline-color);
}
/* DATE DRILLDOWN */
.change-list .toplinks {
display: flex;
padding-bottom: 5px;
flex-wrap: wrap;
gap: 3px 17px;
font-weight: bold;
}
.change-list .toplinks a {
font-size: 0.8125rem;
}
.change-list .toplinks .date-back {
color: var(--body-quiet-color);
}
.change-list .toplinks .date-back:focus,
.change-list .toplinks .date-back:hover {
color: var(--link-hover-color);
}
/* ACTIONS */
.filtered .actions {
border-right: none;
}
#changelist table input {
margin: 0;
vertical-align: baseline;
}
/* Once the :has() pseudo-class is supported by all browsers, the tr.selected
selector and the JS adding the class can be removed. */
#changelist tbody tr.selected {
background-color: var(--selected-row);
}
#changelist tbody tr:has(.action-select:checked) {
background-color: var(--selected-row);
}
#changelist .actions {
padding: 10px;
background: var(--body-bg);
border-top: none;
border-bottom: none;
line-height: 1.5rem;
color: var(--body-quiet-color);
width: 100%;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 0.8125rem;
margin: 0 0.5em;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
vertical-align: top;
height: 1.5rem;
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 0.875rem;
padding: 0 0 0 4px;
margin: 0;
margin-left: 10px;
}
#changelist .actions select:focus {
border-color: var(--body-quiet-color);
}
#changelist .actions label {
display: inline-block;
vertical-align: middle;
font-size: 0.8125rem;
}
#changelist .actions .button {
font-size: 0.8125rem;
border: 1px solid var(--border-color);
border-radius: 4px;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
height: 1.5rem;
line-height: 1;
padding: 4px 8px;
margin: 0;
color: var(--body-fg);
}
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
@media (prefers-color-scheme: dark) {
:root {
--primary: #264b5d;
@ -136,142 +135,3 @@ html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
color: var(--body-fg);
background-color: var(--body-bg);
}
=======
@media (prefers-color-scheme: dark) {
:root {
--primary: #264b5d;
--primary-fg: #f7f7f7;
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
--breadcrumbs-bg: var(--primary);
--link-fg: #81d4fa;
--link-hover-color: #4ac1f7;
--link-selected-fg: #6f94c6;
--hairline-color: #272727;
--border-color: #353535;
--error-fg: #e35f5f;
--message-success-bg: #006b1b;
--message-warning-bg: #583305;
--message-error-bg: #570808;
--darkened-bg: #212121;
--selected-bg: #1b1b1b;
--selected-row: #00363a;
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
}
}
html[data-theme="dark"] {
--primary: #264b5d;
--primary-fg: #f7f7f7;
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
--breadcrumbs-bg: var(--primary);
--link-fg: #81d4fa;
--link-hover-color: #4ac1f7;
--link-selected-fg: #6f94c6;
--hairline-color: #272727;
--border-color: #353535;
--error-fg: #e35f5f;
--message-success-bg: #006b1b;
--message-warning-bg: #583305;
--message-error-bg: #570808;
--darkened-bg: #212121;
--selected-bg: #1b1b1b;
--selected-row: #00363a;
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
}
/* THEME SWITCH */
.theme-toggle {
cursor: pointer;
border: none;
padding: 0;
background: transparent;
vertical-align: middle;
margin-inline-start: 5px;
margin-top: -1px;
}
.theme-toggle svg {
vertical-align: middle;
height: 1rem;
width: 1rem;
display: none;
}
/*
Fully hide screen reader text so we only show the one matching the current
theme.
*/
.theme-toggle .visually-hidden {
display: none;
}
html[data-theme="auto"] .theme-toggle .theme-label-when-auto {
display: block;
}
html[data-theme="dark"] .theme-toggle .theme-label-when-dark {
display: block;
}
html[data-theme="light"] .theme-toggle .theme-label-when-light {
display: block;
}
/* ICONS */
.theme-toggle svg.theme-icon-when-auto,
.theme-toggle svg.theme-icon-when-dark,
.theme-toggle svg.theme-icon-when-light {
fill: var(--header-link-color);
color: var(--header-bg);
}
html[data-theme="auto"] .theme-toggle svg.theme-icon-when-auto {
display: block;
}
html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
display: block;
}
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
display: block;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
color: var(--body-fg);
background-color: var(--body-bg);
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
@ -28,34 +27,3 @@ ul.actionlist li {
overflow: hidden;
text-overflow: ellipsis;
}
=======
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
}
.dashboard .module table th {
width: 100%;
}
.dashboard .module table td {
white-space: nowrap;
}
.dashboard .module table td a {
display: block;
padding-right: .6em;
}
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
margin-left: 0;
}
ul.actionlist li {
list-style-type: none;
overflow: hidden;
text-overflow: ellipsis;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
@import url('widgets.css');
/* FORM ROWS */
@ -529,535 +528,3 @@ form .related-widget-wrapper ul {
.clearable-file-input input {
margin-top: 0;
}
=======
@import url('widgets.css');
/* FORM ROWS */
.form-row {
overflow: hidden;
padding: 10px;
font-size: 0.8125rem;
border-bottom: 1px solid var(--hairline-color);
}
.form-row img, .form-row input {
vertical-align: middle;
}
.form-row label input[type="checkbox"] {
margin-top: 0;
vertical-align: 0;
}
form .form-row p {
padding-left: 0;
}
.flex-container {
display: flex;
flex-wrap: wrap;
}
.form-multiline > div {
padding-bottom: 10px;
}
/* FORM LABELS */
label {
font-weight: normal;
color: var(--body-quiet-color);
font-size: 0.8125rem;
}
.required label, label.required {
font-weight: bold;
color: var(--body-fg);
}
/* RADIO BUTTONS */
form div.radiolist div {
padding-right: 7px;
}
form div.radiolist.inline div {
display: inline-block;
}
form div.radiolist label {
width: auto;
}
form div.radiolist input[type="radio"] {
margin: -2px 4px 0 0;
padding: 0;
}
form ul.inline {
margin-left: 0;
padding: 0;
}
form ul.inline li {
float: left;
padding-right: 7px;
}
/* ALIGNED FIELDSETS */
.aligned label {
display: block;
padding: 4px 10px 0 0;
width: 160px;
word-wrap: break-word;
line-height: 1;
}
.aligned label:not(.vCheckboxLabel):after {
content: '';
display: inline-block;
vertical-align: middle;
height: 1.625rem;
}
.aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly {
padding: 6px 0;
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
overflow-wrap: break-word;
}
.aligned ul label {
display: inline;
float: none;
width: auto;
}
.aligned .form-row input {
margin-bottom: 0;
}
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
width: 350px;
}
form .aligned ul {
margin-left: 160px;
padding-left: 10px;
}
form .aligned div.radiolist {
display: inline-block;
margin: 0;
padding: 0;
}
form .aligned p.help,
form .aligned div.help {
margin-top: 0;
margin-left: 160px;
padding-left: 10px;
}
form .aligned p.date div.help.timezonewarning,
form .aligned p.datetime div.help.timezonewarning,
form .aligned p.time div.help.timezonewarning {
margin-left: 0;
padding-left: 0;
font-weight: normal;
}
form .aligned p.help:last-child,
form .aligned div.help:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
form .aligned input + p.help,
form .aligned textarea + p.help,
form .aligned select + p.help,
form .aligned input + div.help,
form .aligned textarea + div.help,
form .aligned select + div.help {
margin-left: 160px;
padding-left: 10px;
}
form .aligned ul li {
list-style: none;
}
form .aligned table p {
margin-left: 0;
padding-left: 0;
}
.aligned .vCheckboxLabel {
float: none;
width: auto;
display: inline-block;
vertical-align: -3px;
padding: 0 0 5px 5px;
}
.aligned .vCheckboxLabel + p.help,
.aligned .vCheckboxLabel + div.help {
margin-top: -4px;
}
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
width: 610px;
}
fieldset .fieldBox {
margin-right: 20px;
}
/* WIDE FIELDSETS */
.wide label {
width: 200px;
}
form .wide p,
form .wide ul.errorlist,
form .wide input + p.help,
form .wide input + div.help {
margin-left: 200px;
}
form .wide p.help,
form .wide div.help {
padding-left: 50px;
}
form div.help ul {
padding-left: 0;
margin-left: 0;
}
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
width: 450px;
}
/* COLLAPSED FIELDSETS */
fieldset.collapsed * {
display: none;
}
fieldset.collapsed h2, fieldset.collapsed {
display: block;
}
fieldset.collapsed {
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
}
fieldset.collapsed h2 {
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
fieldset .collapse-toggle {
color: var(--header-link-color);
}
fieldset.collapsed .collapse-toggle {
background: transparent;
display: inline;
color: var(--link-fg);
}
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: var(--font-family-monospace);
}
/* SUBMIT ROW */
.submit-row {
padding: 12px 14px 12px;
margin: 0 0 20px;
background: var(--darkened-bg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
body.popup .submit-row {
overflow: auto;
}
.submit-row input {
height: 2.1875rem;
line-height: 0.9375rem;
}
.submit-row input, .submit-row a {
margin: 0;
}
.submit-row input.default {
text-transform: uppercase;
}
.submit-row a.deletelink {
margin-left: auto;
}
.submit-row a.deletelink {
display: block;
background: var(--delete-button-bg);
border-radius: 4px;
padding: 0.625rem 0.9375rem;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.closelink {
display: inline-block;
background: var(--close-button-bg);
border-radius: 4px;
padding: 10px 15px;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.deletelink:focus,
.submit-row a.deletelink:hover,
.submit-row a.deletelink:active {
background: var(--delete-button-hover-bg);
text-decoration: none;
}
.submit-row a.closelink:focus,
.submit-row a.closelink:hover,
.submit-row a.closelink:active {
background: var(--close-button-hover-bg);
text-decoration: none;
}
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
vertical-align: top;
}
.vCheckboxField {
border: none;
}
.vDateField, .vTimeField {
margin-right: 2px;
margin-bottom: 4px;
}
.vDateField {
min-width: 6.85em;
}
.vTimeField {
min-width: 4.7em;
}
.vURLField {
width: 30em;
}
.vLargeTextField, .vXMLLargeTextField {
width: 48em;
}
.flatpages-flatpage #id_content {
height: 40.2em;
}
.module table .vPositiveSmallIntegerField {
width: 2.2em;
}
.vIntegerField {
width: 5em;
}
.vBigIntegerField {
width: 10em;
}
.vForeignKeyRawIdAdminField {
width: 5em;
}
.vTextField, .vUUIDField {
width: 20em;
}
/* INLINES */
.inline-group {
padding: 0;
margin: 0 0 30px;
}
.inline-group thead th {
padding: 8px 10px;
}
.inline-group .aligned label {
width: 160px;
}
.inline-related {
position: relative;
}
.inline-related h3 {
margin: 0;
color: var(--body-quiet-color);
padding: 5px;
font-size: 0.8125rem;
background: var(--darkened-bg);
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
}
.inline-related h3 span.delete {
float: right;
}
.inline-related h3 span.delete label {
margin-left: 2px;
font-size: 0.6875rem;
}
.inline-related fieldset {
margin: 0;
background: var(--body-bg);
border: none;
width: 100%;
}
.inline-related fieldset.module h3 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 0.6875rem;
text-align: left;
font-weight: bold;
background: #bcd;
color: var(--body-bg);
}
.inline-group .tabular fieldset.module {
border: none;
}
.inline-related.tabular fieldset.module table {
width: 100%;
overflow-x: scroll;
}
.last-related fieldset {
border: none;
}
.inline-group .tabular tr.has_original td {
padding-top: 2em;
}
.inline-group .tabular tr td.original {
padding: 2px 0 0 0;
width: 0;
_position: relative;
}
.inline-group .tabular th.original {
width: 0px;
padding: 0;
}
.inline-group .tabular td.original p {
position: absolute;
left: 0;
height: 1.1em;
padding: 2px 9px;
overflow: hidden;
font-size: 0.5625rem;
font-weight: bold;
color: var(--body-quiet-color);
_width: 700px;
}
.inline-group ul.tools {
padding: 0;
margin: 0;
list-style: none;
}
.inline-group ul.tools li {
display: inline;
padding: 0 5px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
color: var(--body-quiet-color);
background: var(--darkened-bg);
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group .tabular tr.add-row td {
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group ul.tools a.add,
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
padding-left: 16px;
font-size: 0.75rem;
}
.empty-form {
display: none;
}
/* RELATED FIELD ADD ONE / LOOKUP */
.related-lookup {
margin-left: 5px;
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
background-size: 14px;
}
.related-lookup {
width: 1rem;
height: 1rem;
background-image: url(../img/search.svg);
}
form .related-widget-wrapper ul {
display: inline-block;
margin-left: 0;
padding-left: 0;
}
.clearable-file-input input {
margin-top: 0;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* LOGIN FORM */
.login {
@ -60,66 +59,3 @@
.login .password-reset-link {
text-align: center;
}
=======
/* LOGIN FORM */
.login {
background: var(--darkened-bg);
height: auto;
}
.login #header {
height: auto;
padding: 15px 16px;
justify-content: center;
}
.login #header h1 {
font-size: 1.125rem;
margin: 0;
}
.login #header h1 a {
color: var(--header-link-color);
}
.login #content {
padding: 20px 20px 0;
}
.login #container {
background: var(--body-bg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
width: 28em;
min-width: 300px;
margin: 100px auto;
height: auto;
}
.login .form-row {
padding: 4px 0;
}
.login .form-row label {
display: block;
line-height: 2em;
}
.login .form-row #id_username, .login .form-row #id_password {
padding: 8px;
width: 100%;
box-sizing: border-box;
}
.login .submit-row {
padding: 1em 0 0 0;
margin: 0;
text-align: center;
}
.login .password-reset-link {
text-align: center;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
.sticky {
position: sticky;
top: 0;
@ -143,149 +142,3 @@
#nav-sidebar table {
width: 100%;
}
=======
.sticky {
position: sticky;
top: 0;
max-height: 100vh;
}
.toggle-nav-sidebar {
z-index: 20;
left: 0;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 23px;
width: 23px;
border: 0;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
cursor: pointer;
font-size: 1.25rem;
color: var(--link-fg);
padding: 0;
}
[dir="rtl"] .toggle-nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
}
.toggle-nav-sidebar:hover,
.toggle-nav-sidebar:focus {
background-color: var(--darkened-bg);
}
#nav-sidebar {
z-index: 15;
flex: 0 0 275px;
left: -276px;
margin-left: -276px;
border-top: 1px solid transparent;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
overflow: auto;
}
[dir="rtl"] #nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
left: 0;
margin-left: 0;
right: -276px;
margin-right: -276px;
}
.toggle-nav-sidebar::before {
content: '\00BB';
}
.main.shifted .toggle-nav-sidebar::before {
content: '\00AB';
}
.main > #nav-sidebar {
visibility: hidden;
}
.main.shifted > #nav-sidebar {
margin-left: 0;
visibility: visible;
}
[dir="rtl"] .main.shifted > #nav-sidebar {
margin-right: 0;
}
#nav-sidebar .module th {
width: 100%;
overflow-wrap: anywhere;
}
#nav-sidebar .module th,
#nav-sidebar .module caption {
padding-left: 16px;
}
#nav-sidebar .module td {
white-space: nowrap;
}
[dir="rtl"] #nav-sidebar .module th,
[dir="rtl"] #nav-sidebar .module caption {
padding-left: 8px;
padding-right: 16px;
}
#nav-sidebar .current-app .section:link,
#nav-sidebar .current-app .section:visited {
color: var(--header-color);
font-weight: bold;
}
#nav-sidebar .current-model {
background: var(--selected-row);
}
.main > #nav-sidebar + .content {
max-width: calc(100% - 23px);
}
.main.shifted > #nav-sidebar + .content {
max-width: calc(100% - 299px);
}
@media (max-width: 767px) {
#nav-sidebar, #toggle-nav-sidebar {
display: none;
}
.main > #nav-sidebar + .content,
.main.shifted > #nav-sidebar + .content {
max-width: 100%;
}
}
#nav-filter {
width: 100%;
box-sizing: border-box;
padding: 2px 5px;
margin: 5px 0;
border: 1px solid var(--border-color);
background-color: var(--darkened-bg);
color: var(--body-fg);
}
#nav-filter:focus {
border-color: var(--body-quiet-color);
}
#nav-filter.no-results {
background: var(--message-error-bg);
}
#nav-sidebar table {
width: 100%;
}
>>>>>>> master

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* TABLETS */
@media (max-width: 1024px) {
@ -80,86 +79,3 @@
margin-right: 0;
}
}
=======
/* TABLETS */
@media (max-width: 1024px) {
[dir="rtl"] .colMS {
margin-right: 0;
}
[dir="rtl"] #user-tools {
text-align: right;
}
[dir="rtl"] #changelist .actions label {
padding-left: 10px;
padding-right: 0;
}
[dir="rtl"] #changelist .actions select {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .change-list .filtered .results,
[dir="rtl"] .change-list .filtered .paginator,
[dir="rtl"] .filtered #toolbar,
[dir="rtl"] .filtered div.xfull,
[dir="rtl"] .filtered .actions,
[dir="rtl"] #changelist-filter {
margin-left: 0;
}
[dir="rtl"] .inline-group ul.tools a.add,
[dir="rtl"] .inline-group div.add-row a,
[dir="rtl"] .inline-group .tabular tr.add-row td a {
padding: 8px 26px 8px 10px;
background-position: calc(100% - 8px) 9px;
}
[dir="rtl"] .related-widget-wrapper-link + .selector {
margin-right: 0;
margin-left: 15px;
}
[dir="rtl"] .selector .selector-filter label {
margin-right: 0;
margin-left: 8px;
}
[dir="rtl"] .object-tools li {
float: right;
}
[dir="rtl"] .object-tools li + li {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .dashboard .module table td a {
padding-left: 0;
padding-right: 16px;
}
}
/* MOBILE */
@media (max-width: 767px) {
[dir="rtl"] .aligned .related-lookup,
[dir="rtl"] .aligned .datetimeshortcuts {
margin-left: 0;
margin-right: 15px;
}
[dir="rtl"] .aligned ul,
[dir="rtl"] form .aligned ul.errorlist {
margin-right: 0;
}
[dir="rtl"] #changelist-filter {
margin-left: 0;
margin-right: 0;
}
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* GLOBAL */
th {
@ -287,293 +286,3 @@ form .form-row p.datetime {
margin-left: inherit;
margin-right: 2px;
}
=======
/* GLOBAL */
th {
text-align: right;
}
.module h2, .module caption {
text-align: right;
}
.module ul, .module ol {
margin-left: 0;
margin-right: 1.5em;
}
.viewlink, .addlink, .changelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
.deletelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
.object-tools {
float: left;
}
thead th:first-child,
tfoot td:first-child {
border-left: none;
}
/* LAYOUT */
#user-tools {
right: auto;
left: 0;
text-align: left;
}
div.breadcrumbs {
text-align: right;
}
#content-main {
float: right;
}
#content-related {
float: left;
margin-left: -300px;
margin-right: auto;
}
.colMS {
margin-left: 300px;
margin-right: 0;
}
/* SORTABLE TABLES */
table thead th.sorted .sortoptions {
float: left;
}
thead th.sorted .text {
padding-right: 0;
padding-left: 42px;
}
/* dashboard styles */
.dashboard .module table td a {
padding-left: .6em;
padding-right: 16px;
}
/* changelists styles */
.change-list .filtered table {
border-left: none;
border-right: 0px none;
}
#changelist-filter {
border-left: none;
border-right: none;
margin-left: 0;
margin-right: 30px;
}
#changelist-filter li.selected {
border-left: none;
padding-left: 10px;
margin-left: 0;
border-right: 5px solid var(--hairline-color);
padding-right: 10px;
margin-right: -15px;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-right: none;
border-left: none;
}
/* FORMS */
.aligned label {
padding: 0 0 3px 1em;
}
.submit-row a.deletelink {
margin-left: 0;
margin-right: auto;
}
.vDateField, .vTimeField {
margin-left: 2px;
}
.aligned .form-row input {
margin-left: 5px;
}
form .aligned ul {
margin-right: 163px;
padding-right: 10px;
margin-left: 0;
padding-left: 0;
}
form ul.inline li {
float: right;
padding-right: 0;
padding-left: 7px;
}
form .aligned p.help,
form .aligned div.help {
margin-right: 160px;
padding-right: 10px;
}
form div.help ul,
form .aligned .checkbox-row + .help,
form .aligned p.date div.help.timezonewarning,
form .aligned p.datetime div.help.timezonewarning,
form .aligned p.time div.help.timezonewarning {
margin-right: 0;
padding-right: 0;
}
form .wide p.help, form .wide div.help {
padding-left: 0;
padding-right: 50px;
}
form .wide p,
form .wide ul.errorlist,
form .wide input + p.help,
form .wide input + div.help {
margin-right: 200px;
margin-left: 0px;
}
.submit-row {
text-align: right;
}
fieldset .fieldBox {
margin-left: 20px;
margin-right: 0;
}
.errorlist li {
background-position: 100% 12px;
padding: 0;
}
.errornote {
background-position: 100% 12px;
padding: 10px 12px;
}
/* WIDGETS */
.calendarnav-previous {
top: 0;
left: auto;
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -45px;
}
.calendarnav-next {
top: 0;
right: auto;
left: 10px;
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -15px;
}
.calendar caption, .calendarbox h2 {
text-align: center;
}
.selector {
float: right;
}
.selector .selector-filter {
text-align: right;
}
.selector-add {
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}
.active.selector-add:focus, .active.selector-add:hover {
background-position: 0 -80px;
}
.selector-remove {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}
.active.selector-remove:focus, .active.selector-remove:hover {
background-position: 0 -112px;
}
a.selector-chooseall {
background: url(../img/selector-icons.svg) right -128px no-repeat;
}
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
background-position: 100% -144px;
}
a.selector-clearall {
background: url(../img/selector-icons.svg) 0 -160px no-repeat;
}
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
background-position: 0 -176px;
}
.inline-deletelink {
float: left;
}
form .form-row p.datetime {
overflow: hidden;
}
.related-widget-wrapper {
float: right;
}
/* MISC */
.inline-related h2, .inline-group h2 {
text-align: right
}
.inline-related h3 span.delete {
padding-right: 20px;
padding-left: inherit;
left: 10px;
right: inherit;
float:left;
}
.inline-related h3 span.delete label {
margin-left: inherit;
margin-right: 2px;
}
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
The MIT License (MIT)
Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
@ -20,26 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=======
The MIT License (MIT)
Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
>>>>>>> master

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
.select2-container {
box-sizing: border-box;
display: inline-block;
@ -480,486 +479,3 @@
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }
=======
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle; }
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-selection--single .select2-selection__clear {
position: relative; }
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px; }
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-search--inline {
float: left; }
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0; }
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051; }
.select2-results {
display: block; }
.select2-results__options {
list-style: none;
margin: 0;
padding: 0; }
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none; }
.select2-results__option[aria-selected] {
cursor: pointer; }
.select2-container--open .select2-dropdown {
left: 0; }
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-search--dropdown {
display: block;
padding: 4px; }
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box; }
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-search--dropdown.select2-search--hide {
display: none; }
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0); }
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important; }
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold; }
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto; }
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none; }
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px;
padding: 1px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0; }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none; }
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa; }
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield; }
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--default .select2-results__option[role=group] {
padding: 0; }
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; }
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white; }
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px; }
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto; }
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0; }
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right;
margin-left: 5px;
margin-right: auto; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0; }
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none; }
.select2-container--classic .select2-dropdown {
background-color: white;
border: 1px solid transparent; }
.select2-container--classic .select2-dropdown--above {
border-bottom: none; }
.select2-container--classic .select2-dropdown--below {
border-top: none; }
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--classic .select2-results__option[role=group] {
padding: 0; }
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey; }
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: white; }
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }
>>>>>>> master

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
/* SELECTOR (FILTER INTERFACE) */
.selector {
@ -602,608 +601,3 @@ select + .related-widget-wrapper-link,
width: 600px;
height: 400px;
}
=======
/* SELECTOR (FILTER INTERFACE) */
.selector {
width: 800px;
float: left;
display: flex;
}
.selector select {
width: 380px;
height: 17.2em;
flex: 1 0 auto;
}
.selector-available, .selector-chosen {
width: 380px;
text-align: center;
margin-bottom: 5px;
display: flex;
flex-direction: column;
}
.selector-available h2, .selector-chosen h2 {
border: 1px solid var(--border-color);
border-radius: 4px 4px 0 0;
}
.selector-chosen .list-footer-display {
border: 1px solid var(--border-color);
border-top: none;
border-radius: 0 0 4px 4px;
margin: 0 0 10px;
padding: 8px;
text-align: center;
background: var(--primary);
color: var(--header-link-color);
cursor: pointer;
}
.selector-chosen .list-footer-display__clear {
color: var(--breadcrumbs-fg);
}
.selector-chosen h2 {
background: var(--primary);
color: var(--header-link-color);
}
.selector .selector-available h2 {
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
.selector .selector-filter {
border: 1px solid var(--border-color);
border-width: 0 1px;
padding: 8px;
color: var(--body-quiet-color);
font-size: 0.625rem;
margin: 0;
text-align: left;
}
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
float: left;
margin: 7px 0 0;
width: 18px;
height: 18px;
padding: 0;
overflow: hidden;
line-height: 1;
}
.selector .selector-available input,
.selector .selector-chosen input {
width: 320px;
margin-left: 8px;
}
.selector ul.selector-chooser {
align-self: center;
width: 22px;
background-color: var(--selected-bg);
border-radius: 10px;
margin: 0 5px;
padding: 0;
transform: translateY(-17px);
}
.selector-chooser li {
margin: 0;
padding: 3px;
list-style-type: none;
}
.selector select {
padding: 0 10px;
margin: 0 0 10px;
border-radius: 0 0 4px 4px;
}
.selector .selector-chosen--with-filtered select {
margin: 0;
border-radius: 0;
height: 14em;
}
.selector .selector-chosen:not(.selector-chosen--with-filtered) .list-footer-display {
display: none;
}
.selector-add, .selector-remove {
width: 16px;
height: 16px;
display: block;
text-indent: -3000px;
overflow: hidden;
cursor: default;
opacity: 0.55;
}
.active.selector-add, .active.selector-remove {
opacity: 1;
}
.active.selector-add:hover, .active.selector-remove:hover {
cursor: pointer;
}
.selector-add {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}
.active.selector-add:focus, .active.selector-add:hover {
background-position: 0 -112px;
}
.selector-remove {
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}
.active.selector-remove:focus, .active.selector-remove:hover {
background-position: 0 -80px;
}
a.selector-chooseall, a.selector-clearall {
display: inline-block;
height: 16px;
text-align: left;
margin: 1px auto 3px;
overflow: hidden;
font-weight: bold;
line-height: 16px;
color: var(--body-quiet-color);
text-decoration: none;
opacity: 0.55;
}
a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
color: var(--link-fg);
}
a.active.selector-chooseall, a.active.selector-clearall {
opacity: 1;
}
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
cursor: pointer;
}
a.selector-chooseall {
padding: 0 18px 0 0;
background: url(../img/selector-icons.svg) right -160px no-repeat;
cursor: default;
}
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
background-position: 100% -176px;
}
a.selector-clearall {
padding: 0 0 0 18px;
background: url(../img/selector-icons.svg) 0 -128px no-repeat;
cursor: default;
}
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
background-position: 0 -144px;
}
/* STACKED SELECTORS */
.stacked {
float: left;
width: 490px;
display: block;
}
.stacked select {
width: 480px;
height: 10.1em;
}
.stacked .selector-available, .stacked .selector-chosen {
width: 480px;
}
.stacked .selector-available {
margin-bottom: 0;
}
.stacked .selector-available input {
width: 422px;
}
.stacked ul.selector-chooser {
height: 22px;
width: 50px;
margin: 0 0 10px 40%;
background-color: #eee;
border-radius: 10px;
transform: none;
}
.stacked .selector-chooser li {
float: left;
padding: 3px 3px 3px 5px;
}
.stacked .selector-chooseall, .stacked .selector-clearall {
display: none;
}
.stacked .selector-add {
background: url(../img/selector-icons.svg) 0 -32px no-repeat;
cursor: default;
}
.stacked .active.selector-add {
background-position: 0 -32px;
cursor: pointer;
}
.stacked .active.selector-add:focus, .stacked .active.selector-add:hover {
background-position: 0 -48px;
cursor: pointer;
}
.stacked .selector-remove {
background: url(../img/selector-icons.svg) 0 0 no-repeat;
cursor: default;
}
.stacked .active.selector-remove {
background-position: 0 0px;
cursor: pointer;
}
.stacked .active.selector-remove:focus, .stacked .active.selector-remove:hover {
background-position: 0 -16px;
cursor: pointer;
}
.selector .help-icon {
background: url(../img/icon-unknown.svg) 0 0 no-repeat;
display: inline-block;
vertical-align: middle;
margin: -2px 0 0 2px;
width: 13px;
height: 13px;
}
.selector .selector-chosen .help-icon {
background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
}
.selector .search-label-icon {
background: url(../img/search.svg) 0 0 no-repeat;
display: inline-block;
height: 1.125rem;
width: 1.125rem;
}
/* DATE AND TIME */
p.datetime {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-weight: bold;
}
.datetime span {
white-space: nowrap;
font-weight: normal;
font-size: 0.6875rem;
color: var(--body-quiet-color);
}
.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
margin-left: 5px;
margin-bottom: 4px;
}
table p.datetime {
font-size: 0.6875rem;
margin-left: 0;
padding-left: 0;
}
.datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
position: relative;
display: inline-block;
vertical-align: middle;
height: 16px;
width: 16px;
overflow: hidden;
}
.datetimeshortcuts .clock-icon {
background: url(../img/icon-clock.svg) 0 0 no-repeat;
}
.datetimeshortcuts a:focus .clock-icon,
.datetimeshortcuts a:hover .clock-icon {
background-position: 0 -16px;
}
.datetimeshortcuts .date-icon {
background: url(../img/icon-calendar.svg) 0 0 no-repeat;
top: -1px;
}
.datetimeshortcuts a:focus .date-icon,
.datetimeshortcuts a:hover .date-icon {
background-position: 0 -16px;
}
.timezonewarning {
font-size: 0.6875rem;
color: var(--body-quiet-color);
}
/* URL */
p.url {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-size: 0.6875rem;
font-weight: bold;
}
.url a {
font-weight: normal;
}
/* FILE UPLOADS */
p.file-upload {
line-height: 20px;
margin: 0;
padding: 0;
color: var(--body-quiet-color);
font-size: 0.6875rem;
font-weight: bold;
}
.file-upload a {
font-weight: normal;
}
.file-upload .deletelink {
margin-left: 5px;
}
span.clearable-file-input label {
color: var(--body-fg);
font-size: 0.6875rem;
display: inline;
float: none;
}
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
margin: 5px auto;
font-size: 0.75rem;
width: 19em;
text-align: center;
background: var(--body-bg);
color: var(--body-fg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
}
.clockbox {
width: auto;
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 100%;
}
.calendar caption, .calendarbox h2 {
margin: 0;
text-align: center;
border-top: none;
font-weight: 700;
font-size: 0.75rem;
color: #333;
background: var(--accent);
}
.calendar th {
padding: 8px 5px;
background: var(--darkened-bg);
border-bottom: 1px solid var(--border-color);
font-weight: 400;
font-size: 0.75rem;
text-align: center;
color: var(--body-quiet-color);
}
.calendar td {
font-weight: 400;
font-size: 0.75rem;
text-align: center;
padding: 0;
border-top: 1px solid var(--hairline-color);
border-bottom: none;
}
.calendar td.selected a {
background: var(--primary);
color: var(--button-fg);
}
.calendar td.nonday {
background: var(--darkened-bg);
}
.calendar td.today a {
font-weight: 700;
}
.calendar td a, .timelist a {
display: block;
font-weight: 400;
padding: 6px;
text-decoration: none;
color: var(--body-quiet-color);
}
.calendar td a:focus, .timelist a:focus,
.calendar td a:hover, .timelist a:hover {
background: var(--primary);
color: white;
}
.calendar td a:active, .timelist a:active {
background: var(--header-bg);
color: white;
}
.calendarnav {
font-size: 0.625rem;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link, #calendarnav a:visited,
#calendarnav a:focus, #calendarnav a:hover {
color: var(--body-quiet-color);
}
.calendar-shortcuts {
background: var(--body-bg);
color: var(--body-quiet-color);
font-size: 0.6875rem;
line-height: 0.6875rem;
border-top: 1px solid var(--hairline-color);
padding: 8px 0;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
display: block;
position: absolute;
top: 8px;
width: 15px;
height: 15px;
text-indent: -9999px;
padding: 0;
}
.calendarnav-previous {
left: 10px;
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -15px;
}
.calendarnav-next {
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -45px;
}
.calendar-cancel {
margin: 0;
padding: 4px 0;
font-size: 0.75rem;
background: #eee;
border-top: 1px solid var(--border-color);
color: var(--body-fg);
}
.calendar-cancel:focus, .calendar-cancel:hover {
background: #ddd;
}
.calendar-cancel a {
color: black;
display: block;
}
ul.timelist, .timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}
/* EDIT INLINE */
.inline-deletelink {
float: right;
text-indent: -9999px;
background: url(../img/inline-delete.svg) 0 0 no-repeat;
width: 16px;
height: 16px;
border: 0px none;
}
.inline-deletelink:focus, .inline-deletelink:hover {
cursor: pointer;
}
/* RELATED WIDGET WRAPPER */
.related-widget-wrapper {
float: left; /* display properly in form rows with multiple fields */
overflow: hidden; /* clear floated contents */
}
.related-widget-wrapper-link {
opacity: 0.3;
}
.related-widget-wrapper-link:link {
opacity: .8;
}
.related-widget-wrapper-link:link:focus,
.related-widget-wrapper-link:link:hover {
opacity: 1;
}
select + .related-widget-wrapper-link,
.related-widget-wrapper-link + .related-widget-wrapper-link {
margin-left: 7px;
}
/* GIS MAPS */
.dj_map {
width: 600px;
height: 400px;
}
>>>>>>> master

View File

@ -1,462 +1,90 @@
/*
* article.css
* Styles for CKEditor 5 rendered content inside .content-card
* and for the article detail page layout.
*
* Scope: all rules are under .content-card .card-content
* so they never leak into the rest of the site.
*/
/* ===== ARTICLE PAGE LAYOUT ===== */
.article-meta {
display: flex;
align-items: center;
gap: 1.5rem;
flex-wrap: wrap;
font-size: 0.9rem;
color: var(--text-light);
margin-bottom: 2rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-light);
}
.article-meta span {
display: flex;
align-items: center;
gap: 0.4rem;
}
.article-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1.5rem 0;
}
.article-tag {
display: inline-block;
padding: 0.25rem 0.75rem;
background: var(--bg-tertiary);
border: 1px solid var(--border-light);
border-radius: var(--radius-sm);
font-size: 0.8rem;
color: var(--text-secondary);
text-decoration: none;
transition: var(--transition);
}
.article-tag:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* ===== CKEDITOR 5 CONTENT ===== */
.content-card .card-content {
font-size: 1.05rem;
line-height: 1.8;
color: var(--text-primary);
word-break: break-word;
}
/* --- Headings --- */
.content-card .card-content h2,
.content-card .card-content h3,
.content-card .card-content h4,
.content-card .card-content h5,
.content-card .card-content h6 {
font-weight: 700;
line-height: 1.3;
margin-top: 2rem;
margin-bottom: 0.75rem;
/* Стили для содержимого статьи */
.article-content {
font-size: 1rem;
line-height: 1.6;
color: var(--text-primary);
}
.content-card .card-content h2 {
font-size: 1.75rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-light);
.article-content h1,
.article-content h2,
.article-content h3,
.article-content h4,
.article-content h5,
.article-content h6 {
margin-top: 1.5em;
margin-bottom: 0.75em;
font-weight: 600;
}
.content-card .card-content h3 {
font-size: 1.375rem;
padding-left: 0.75rem;
border-left: 4px solid var(--primary);
}
.content-card .card-content h4 {
font-size: 1.125rem;
color: var(--text-secondary);
}
/* --- Paragraphs --- */
.content-card .card-content p {
margin-bottom: 1.25rem;
}
.content-card .card-content p:last-child {
margin-bottom: 0;
}
/* --- Links --- */
.content-card .card-content a {
color: var(--primary);
text-decoration: underline;
text-decoration-color: rgba(255, 107, 0, 0.4);
text-underline-offset: 3px;
transition: var(--transition);
}
.content-card .card-content a:hover {
color: var(--primary-dark);
text-decoration-color: var(--primary-dark);
}
/* --- Lists --- */
.content-card .card-content ul,
.content-card .card-content ol {
margin: 0 0 1.25rem 0;
padding-left: 1.75rem;
}
.content-card .card-content ul {
list-style: none;
padding-left: 0;
}
.content-card .card-content ul li {
position: relative;
padding-left: 1.5rem;
margin-bottom: 0.5rem;
}
.content-card .card-content ul li::before {
content: '▸';
position: absolute;
left: 0;
color: var(--primary);
font-weight: bold;
}
.content-card .card-content ol li {
margin-bottom: 0.5rem;
}
.content-card .card-content ul ul,
.content-card .card-content ol ol,
.content-card .card-content ul ol,
.content-card .card-content ol ul {
margin-top: 0.5rem;
margin-bottom: 0;
}
/* --- Blockquote --- */
.content-card .card-content blockquote {
margin: 1.5rem 0;
padding: 1.25rem 1.5rem;
.article-content h1 { font-size: 2rem; }
.article-content h2 { font-size: 1.75rem; }
.article-content h3 { font-size: 1.5rem; }
.article-content p {
margin-bottom: 1rem;
}
.article-content ul,
.article-content ol {
margin-bottom: 1rem;
padding-left: 2rem;
}
.article-content li {
margin-bottom: 0.25rem;
}
.article-content blockquote {
border-left: 4px solid var(--primary);
padding: 0.5rem 1rem;
background: var(--bg-secondary);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
color: var(--text-secondary);
margin: 1rem 0;
font-style: italic;
font-size: 1.05rem;
}
.content-card .card-content blockquote p:last-child {
margin-bottom: 0;
}
/* --- Inline code (not inside pre) --- */
/*
* High specificity is fine here because this selector explicitly
* excludes pre > code, so it never touches hljs blocks.
*/
.content-card .card-content :not(pre) > code {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.875em;
background: var(--bg-tertiary);
color: var(--primary-dark);
padding: 0.15em 0.4em;
border-radius: 4px;
border: 1px solid var(--border-light);
word-break: break-all;
}
/* --- Code blocks (CKEditor generates <pre><code class="hljs">) --- */
/*
* ALL pre and pre code rules use :where() zero specificity.
* This guarantees hljs always wins on colors, background, and padding.
*
* Responsibility split:
* article.css layout only: margin, border-radius, overflow, shadow
* hljs CSS everything visual: background, color, padding, font-size
*
* font-size:0 on pre prevents inherited line-height:1.8 from inflating
* the spacing between lines (line-height is computed from font-size,
* so zeroing it on the wrapper element neutralises the inheritance).
* font-size is restored on pre code.
*/
:where(.content-card .card-content) pre {
margin: 1.5rem 0;
padding: 0; /* hljs sets padding:1em on code.hljs — don't double it */
border-radius: var(--radius-md);
overflow-x: auto;
box-shadow: var(--shadow-md);
font-size: 0; /* neutralise inherited line-height */
line-height: 1;
background: none; /* hljs controls background (#fff or #282c34) */
}
:where(.content-card .card-content) pre code {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.9rem; /* restore font-size for hljs to work from */
line-height: 1.5;
word-break: normal;
white-space: pre;
/* NO color, NO background, NO padding — hljs owns these */
}
/* --- Tables --- */
.content-card .card-content .table-wrapper,
.content-card .card-content figure.table {
overflow-x: auto;
margin: 1.5rem 0;
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
.content-card .card-content table {
.article-content table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
margin: 1rem 0;
}
.content-card .card-content th {
background: var(--bg-secondary);
font-weight: 700;
text-align: left;
padding: 0.875rem 1rem;
border-bottom: 2px solid var(--border-medium);
color: var(--text-primary);
white-space: nowrap;
}
.content-card .card-content td {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-light);
color: var(--text-secondary);
vertical-align: top;
}
.content-card .card-content tr:last-child td {
border-bottom: none;
}
.content-card .card-content tbody tr:hover td {
background: var(--bg-secondary);
}
/* CKEditor 5 table with header row class */
.content-card .card-content table.ck-table-resized th,
.content-card .card-content table.ck-table-resized td {
.article-content th,
.article-content td {
border: 1px solid var(--border-light);
padding: 0.5rem;
}
/* --- Images (CKEditor wraps in <figure class="image">) --- */
.content-card .card-content figure.image {
margin: 1.5rem auto;
text-align: center;
max-width: 100%;
.article-content th {
background: var(--bg-secondary);
}
.content-card .card-content figure.image img,
.content-card .card-content img {
.article-content img {
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
cursor: pointer; /* triggers the click-to-enlarge modal */
}
.content-card .card-content figure.image figcaption {
margin-top: 0.5rem;
font-size: 0.875rem;
color: var(--text-light);
font-style: italic;
text-align: center;
}
/* CKEditor alignment classes */
.content-card .card-content figure.image.image-style-align-left {
float: left;
margin: 0.5rem 1.5rem 1rem 0;
max-width: 45%;
}
.content-card .card-content figure.image.image-style-align-right {
float: right;
margin: 0.5rem 0 1rem 1.5rem;
max-width: 45%;
}
/* Clearfix after floated images */
.content-card .card-content::after {
content: '';
display: table;
clear: both;
}
/* --- Horizontal rule --- */
.content-card .card-content hr {
border: none;
height: 2px;
background: linear-gradient(90deg, transparent, var(--border-medium), transparent);
margin: 2rem 0;
}
/* --- Alert/info boxes (CKEditor custom classes if used) --- */
.content-card .card-content .info-box {
padding: 1rem 1.25rem;
border-radius: var(--radius-md);
margin: 1.5rem 0;
border-left: 4px solid var(--accent);
background: rgba(0, 168, 255, 0.08);
color: var(--text-secondary);
font-size: 0.95rem;
}
/* ===== COMMENTS SECTION ===== */
.comments-section {
margin-top: 3rem;
}
.comments-section h3 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.75rem;
}
.comment-card {
background: var(--bg-secondary);
border-radius: var(--radius-lg);
padding: 1.25rem 1.5rem;
margin-bottom: 1rem;
border: 1px solid var(--border-light);
border-left: 3px solid var(--secondary);
}
.comment-author {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
}
.comment-date {
font-size: 0.8rem;
color: var(--text-light);
margin-left: 0.75rem;
}
.comment-body {
margin-top: 0.75rem;
color: var(--text-secondary);
line-height: 1.6;
}
.comment-form-card {
background: var(--bg-card);
border-radius: var(--radius-xl);
padding: 2rem;
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
margin-top: 2rem;
position: relative;
}
.comment-form-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--gradient-secondary);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.comment-form-card h4 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: var(--text-primary);
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.content-card .card-content h2 {
font-size: 1.5rem;
}
.content-card .card-content h3 {
font-size: 1.25rem;
}
.content-card .card-content figure.image.image-style-align-left,
.content-card .card-content figure.image.image-style-align-right {
float: none;
max-width: 100%;
margin: 1rem auto;
}
/* :where() keeps specificity at zero so hljs still wins on mobile */
:where(.content-card .card-content) pre {
box-shadow: none;
margin: 1rem 0;
}
.content-card .card-content th,
.content-card .card-content td {
padding: 0.6rem 0.75rem;
font-size: 0.875rem;
}
.article-meta {
gap: 1rem;
font-size: 0.8rem;
}
border-radius: 4px;
}
@media (max-width: 480px) {
.content-card .card-content {
font-size: 1rem;
}
.content-card .card-content h2 {
font-size: 1.375rem;
}
.content-card .card-content h3 {
font-size: 1.125rem;
}
.article-content pre {
background: #2d2d2d;
color: #ccc;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
margin: 1rem 0;
}
.article-content code {
font-family: 'Courier New', monospace;
background: #2d2d2d;
color: #ccc;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}
.article-content pre code {
padding: 0;
background: none;
color: inherit;
}

View File

@ -0,0 +1,237 @@
/*
* ck_content.css общие стили для CKEditor-контента
* Используется в блоге (.article-content) и в продуктах (.product-description).
* Подключать вместо article_content.css / product_content.css.
*
* Размещение: static/core/css/ck_content.css (или аналогичное shared-место)
*/
/* ===== Базовый контейнер ===== */
.article-content,
.product-description {
font-size: 1rem;
line-height: 1.7;
color: var(--text-primary);
}
/* ===== Заголовки ===== */
.article-content h1, .product-description h1,
.article-content h2, .product-description h2,
.article-content h3, .product-description h3,
.article-content h4, .product-description h4,
.article-content h5, .product-description h5,
.article-content h6, .product-description h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
font-weight: 600;
line-height: 1.25;
color: var(--text-primary);
}
.article-content h1, .product-description h1 { font-size: 2rem; }
.article-content h2, .product-description h2 { font-size: 1.75rem; }
.article-content h3, .product-description h3 { font-size: 1.5rem; }
.article-content h4, .product-description h4 { font-size: 1.25rem; }
.article-content h5, .product-description h5 { font-size: 1.1rem; }
.article-content h6, .product-description h6 { font-size: 1rem; }
/* ===== Абзацы ===== */
.article-content p,
.product-description p {
margin-bottom: 1.2em;
}
/* ===== Списки ===== */
.article-content ul, .product-description ul,
.article-content ol, .product-description ol {
margin-bottom: 1rem;
padding-left: 2rem;
}
.article-content li,
.product-description li {
margin-bottom: 0.3rem;
}
/* ===== Цитаты ===== */
.article-content blockquote,
.product-description blockquote {
border-left: 4px solid var(--primary);
padding: 0.5rem 1rem;
background: var(--bg-secondary);
margin: 1.25rem 0;
font-style: italic;
color: var(--text-secondary);
}
/* ===== Горизонтальная линия ===== */
.article-content hr,
.product-description hr {
border: none;
border-top: 2px solid var(--border-light);
margin: 2em 0;
}
/* ===== Таблицы ===== */
.article-content table,
.product-description table {
width: 100%;
border-collapse: collapse;
margin: 1.25rem 0;
font-size: 0.95rem;
}
.article-content th, .product-description th,
.article-content td, .product-description td {
border: 1px solid var(--border-light);
padding: 0.6rem 0.8rem;
text-align: left;
}
.article-content th,
.product-description th {
background: var(--bg-secondary);
color: var(--text-primary);
font-weight: 600;
}
.article-content tr:nth-child(even),
.product-description tr:nth-child(even) {
background: var(--bg-secondary);
}
/* ===== Изображения ===== */
.article-content img,
.product-description img {
max-width: 100%;
height: auto;
margin: 1rem 0;
border-radius: var(--radius-sm, 4px);
}
/* CKEditor 5: figure.image */
.article-content figure.image,
.product-description figure.image {
display: inline-block;
margin: 1em 0;
max-width: 100%;
}
.article-content figure.image img,
.product-description figure.image img {
max-width: 100%;
height: auto;
}
.article-content figure.image figcaption,
.product-description figure.image figcaption {
text-align: center;
font-size: 0.88em;
color: var(--text-light);
margin-top: 0.4em;
}
/* Выравнивание изображений (классы CKEditor 5) */
.article-content .image-style-align-left,
.product-description .image-style-align-left {
float: left;
margin-right: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-right,
.product-description .image-style-align-right {
float: right;
margin-left: 1.5em;
margin-bottom: 1em;
max-width: 50%;
}
.article-content .image-style-align-center,
.product-description .image-style-align-center {
display: block;
margin-left: auto;
margin-right: auto;
max-width: 100%;
}
/* clearfix после float-изображений */
.article-content::after,
.product-description::after {
content: '';
display: table;
clear: both;
}
/* ===== Встроенное видео (iframe/media) ===== */
.article-content .media,
.product-description .media {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
margin: 1.5em 0;
}
.article-content .media iframe,
.product-description .media iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
border-radius: var(--radius-sm, 4px);
}
/* ===== Блоки кода ===== */
.article-content pre,
.product-description pre {
background: #1e1e1e;
color: #d4d4d4;
padding: 1rem 1.25rem;
border-radius: var(--radius-sm, 4px);
overflow-x: auto;
margin: 1rem 0;
font-size: 0.9em;
line-height: 1.5;
}
.article-content code,
.product-description code {
font-family: 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
background: var(--bg-secondary);
color: var(--primary);
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.88em;
}
.article-content pre code,
.product-description pre code {
padding: 0;
background: none;
color: inherit;
}
/* ===== Ссылки внутри контента ===== */
.article-content a,
.product-description a {
color: var(--primary);
text-decoration: underline;
text-underline-offset: 3px;
transition: opacity 0.15s;
}
.article-content a:hover,
.product-description a:hover {
opacity: 0.75;
}
/* ===== Адаптивность ===== */
@media (max-width: 600px) {
.article-content .image-style-align-left,
.article-content .image-style-align-right,
.product-description .image-style-align-left,
.product-description .image-style-align-right {
float: none;
max-width: 100%;
margin: 1em 0;
}
.article-content table,
.product-description table {
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}

View File

@ -0,0 +1,290 @@
/*
* products.css layout и компоненты страниц продуктов.
* Типографика CKEditor-контента вынесена в ck_content.css.
*/
/* ===== Сетка продуктов ===== */
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
/* ===== Карточка продукта ===== */
.product-card {
background: var(--bg-card);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
transition: var(--transition);
overflow: hidden;
display: flex;
flex-direction: column;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.product-image {
width: 100%;
height: 180px;
object-fit: cover;
}
.product-image-placeholder {
width: 100%;
height: 180px;
background: var(--bg-secondary);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-light);
font-size: 2rem;
}
.product-image-placeholder::after {
content: '📦';
}
.product-info {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
}
.product-info h3 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
font-weight: 600;
}
.product-info h3 a {
color: var(--text-primary);
text-decoration: none;
transition: color 0.15s;
}
.product-info h3 a:hover {
color: var(--primary);
}
.product-short {
color: var(--text-secondary);
font-size: 0.9rem;
margin-bottom: 0.75rem;
flex: 1;
line-height: 1.5;
}
.product-meta {
font-size: 0.8rem;
color: var(--text-light);
margin-bottom: 0.75rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.product-category,
.product-configs {
background: var(--bg-secondary);
padding: 0.15rem 0.5rem;
border-radius: var(--radius-sm);
border: 1px solid var(--border-light);
}
/* ===== Цена ===== */
.product-price-block {
margin-top: auto;
}
.product-price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary);
}
.price-restricted {
background: var(--bg-secondary);
padding: 0.6rem;
border-radius: var(--radius-sm);
text-align: center;
}
.price-text {
display: block;
margin-bottom: 0.5rem;
font-size: 0.85rem;
color: var(--text-light);
}
.price-actions {
display: flex;
gap: 0.5rem;
justify-content: center;
}
.btn-small {
padding: 0.4rem 0.8rem;
font-size: 0.85rem;
}
/* ===== Фильтры ===== */
.filters-bar {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1.5rem;
align-items: center;
}
.filter-group {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.filter-group > span {
font-size: 0.85rem;
color: var(--text-light);
font-weight: 500;
}
.filter-btn {
padding: 0.3rem 0.8rem;
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-secondary);
text-decoration: none;
font-size: 0.85rem;
border: 1px solid var(--border-light);
transition: var(--transition);
white-space: nowrap;
}
.filter-btn.active,
.filter-btn:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* ===== Детальная страница ===== */
.product-detail {
max-width: 800px;
margin: 0 auto;
}
.product-header {
margin-bottom: 1.5rem;
}
.product-header h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
color: var(--text-primary);
line-height: 1.2;
}
.product-meta-detail {
display: flex;
gap: 0.75rem;
color: var(--text-light);
font-size: 0.875rem;
flex-wrap: wrap;
}
.product-detail-image {
width: 100%;
max-height: 420px;
object-fit: cover;
border-radius: var(--radius-md);
margin-bottom: 2rem;
}
/* Контент CKEditor — max-width задан на .product-detail, не здесь */
.product-description {
margin-bottom: 2rem;
}
/* ===== Блок цены (детальная) ===== */
.product-price-block-detail {
margin: 2rem 0;
text-align: center;
}
.product-price-large {
font-size: 2rem;
font-weight: 700;
color: var(--primary);
display: block;
margin-bottom: 1rem;
}
.price-restricted-detail {
background: var(--bg-secondary);
padding: 1.5rem;
border-radius: var(--radius-md);
border: 1px solid var(--border-light);
}
.price-restricted-detail p {
margin-bottom: 1rem;
color: var(--text-secondary);
}
.product-back {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border-light);
}
/* ===== Пагинация ===== */
.pagination {
display: flex;
justify-content: center;
gap: 0.4rem;
margin-top: 2.5rem;
flex-wrap: wrap;
}
.pagination a,
.pagination span {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2.2rem;
padding: 0.45rem 0.7rem;
border-radius: var(--radius-sm);
background: var(--bg-secondary);
color: var(--text-secondary);
text-decoration: none;
font-size: 0.9rem;
border: 1px solid var(--border-light);
transition: var(--transition);
}
.pagination a:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.pagination .current {
background: var(--primary);
color: white;
font-weight: 600;
border-color: var(--primary);
}
.pagination .disabled {
opacity: 0.45;
cursor: default;
pointer-events: none;
}
/* ===== Адаптивность ===== */
@media (max-width: 768px) {
.product-header h1 {
font-size: 1.6rem;
}
.product-price-large {
font-size: 1.6rem;
}
}
@media (max-width: 480px) {
.products-grid {
grid-template-columns: 1fr;
}
.price-actions {
flex-direction: column;
align-items: stretch;
}
.filters-bar {
flex-direction: column;
align-items: flex-start;
}
}

View File

@ -914,6 +914,14 @@ body {
margin: 0 auto;
}
.product-configs {
color: var(--text-secondary);
font-size: 1.25rem;
font-weight: 500;
max-width: 600px;
margin: 0 auto;
}
/* ===== FOOTER ===== */
.footer {
background: var(--footer-bg);
@ -1665,6 +1673,11 @@ body {
padding: 0 1rem;
}
.product-configs {
font-size: 1.125rem;
padding: 0 1rem;
}
.grid-2 {
grid-template-columns: 1fr;
gap: 1.5rem;

View File

@ -16,9 +16,17 @@ const CallbackModal = {
}, 5000);
},
open() {
open(productTitle) {
const modal = document.getElementById(this.modalId);
if (modal) {
if (productTitle) {
const questionField = modal.querySelector('#id_question');
if (questionField) {
questionField.value = `Запрос цены по ПП "${productTitle}"`;
}
}
modal.style.display = 'block';
const floatingBtn = document.getElementById(this.floatingBtnId);
if (floatingBtn && this.shown) {

View File

@ -0,0 +1,30 @@
// programmer/static/programmer/js/profile.js
function openModalContent(el) {
const url = el.dataset.url;
const title = el.querySelector('h4').innerText;
const modal = document.getElementById('contentModal');
const body = document.getElementById('contentModalBody');
const titleEl = document.getElementById('contentModalTitle');
titleEl.textContent = title;
body.innerHTML = '<p>Загрузка...</p>';
modal.style.display = 'block';
fetch(url)
.then(response => response.text())
.then(html => {
body.innerHTML = html;
})
.catch(() => {
body.innerHTML = '<p style="color:red;">Ошибка загрузки. Попробуйте позже.</p>';
});
}
function closeContentModal() {
document.getElementById('contentModal').style.display = 'none';
}
// Закрытие по клику на фон и ESC
document.addEventListener('click', function(e) {
if (e.target === document.getElementById('contentModal')) closeContentModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeContentModal();
});