Коммит a48aef2d создал по автору Кабанин Денис Андреевич's avatar Кабанин Денис Андреевич
Просмотр файлов

мерж с 160

владельцы 67f4d02a 3639501d
......@@ -7,9 +7,7 @@ def allowed_users(allowed_roles=None):
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
group = None
if request.user.groups.exists():
group = request.user.groups.all()[0].name
group = request.user.role
if group in allowed_roles:
return view_func(request, *args, **kwargs)
return HttpResponsePermanentRedirect('/announcements')
......
......@@ -6,15 +6,16 @@ from .models import Announcement
class AnnouncementForm(forms.Form):
date_of_expiring = forms.DateField(label='', widget=forms.DateInput(attrs={'class': 'date', 'type': 'date'}), required=False)
title = forms.CharField(label='', required=True, widget=forms.TextInput(attrs={'class': 'title', 'placeholder' : 'Заголовок объявления'}))
body = forms.CharField(label='', widget=forms.Textarea(attrs={'class': 'body'}))
date_of_expiring = forms.DateField(label='', widget=forms.DateInput(attrs={'class': 'date', 'type': 'date', 'onchange' : 'preview_update(this, \'date\')'}), required=False)
title = forms.CharField(label='', required=True, widget=forms.Textarea(attrs={'class': 'label-textarea', 'placeholder' : 'Заголовок объявления', 'onkeyup' : 'textarea_size(this); preview_update(this, \'title\')', 'cols' : 'none', 'rows' : 'none'}))
body = forms.CharField(label='', required=True, widget=forms.Textarea(attrs={'class': 'label-textarea', 'onkeyup' : 'textarea_size(this); preview_update(this, \'descr\')', 'cols' : 'none', 'rows' : 'none'}))
is_pinned = forms.BooleanField(label='Закрепить', required=False)
image_url = forms.FilePathField(label='Выбрать обложку', path=finders.find("img/announcements/covers"), required=False, widget=forms.TextInput(attrs={'class': 'imurl'}))
files = forms.FileField(label='Прикрепить файлы', widget=forms.FileInput(attrs={'class':"files"}), required=False)
file_id_to_delete = forms.IntegerField(widget=forms.HiddenInput(attrs={'class': 'fitd'}), required=False)
image_url = forms.CharField(label='', required=False, widget=forms.HiddenInput())
files = forms.FileField(label='Прикрепить файлы', widget=forms.FileInput(attrs={'class':"file-input", "id" : 'file', "multiple" : ''}), required=False)
file_id_to_delete = forms.IntegerField(widget=forms.HiddenInput(attrs={'class': 'fitd', 'value' : '-1'}), required=False)
def clean_date_of_expiring(self):
exdate = self.cleaned_data.get('date_of_expiring')
if exdate is not None and datetime.now().date() > exdate:
raise ValidationError('Неверная дата')
return exdate
......@@ -4,7 +4,7 @@ from announcements.models import Announcement
class Command(BaseCommand):
help = 'Deletes expired announcements and their related images, files and tags'
help = 'Deletes expired announcements and their related images and files'
def handle(self, *args, **options):
now = datetime.now(timezone.utc)
......
<!-- Временный файл. Нужен для проверки работы -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Редактор объявлений</title>
</head>
<body>
<form method="post" action="editannouncement/{{ announcement.id }}" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<p>Удалить файлы:</p>
{% for file in announcement.files.all %}
<div class="file-item">
<a href="{{ file.file.url }}">{{ file.file }}</a>
<input type="checkbox" name="file_id_to_delete[]" value="{{ file.id }}">
</div>
{% endfor %}
<input type="submit" value="Изменить" />
</form>
</body>
</html>
\ Нет новой строки в конце файла
<!-- Временный файл. Нужен для проверки работы -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Редактор объявлений</title>
</head>
<body>
<form method="post" action="createannouncement" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Разместить" />
</form>
</body>
</html>
\ Нет новой строки в конце файла
from django import template
register = template.Library()
@register.filter
def split(value):
return value.split('/')[-1]
......@@ -7,6 +7,7 @@ urlpatterns = [
path('createannouncement', views.createannouncement, name='createannouncement'),
path('redactor/<int:id>', views.editor, name='editor'),
path('redactor/editannouncement/<int:id>', views.editannouncement, name='editannouncement'),
path('search', views.search, name='search'),
path('search/', views.search, name='search'),
path('<int:id>', views.announcement, name='announcement'),
path('delete/<int:id>', views.delete_announcement, name='delete')
]
\ Нет новой строки в конце файла
......@@ -6,56 +6,60 @@ from datetime import date
from .decorators import allowed_users
from .models import File
from .forms import AnnouncementForm
from django.core.paginator import Paginator
from django.conf import settings
from pathlib import Path
import os
# Добавить проверку форм при удалении тестовых шаблонов
# В функию index надо передавать anid - максимальное количество следующих объявлений на странице, начиная с 40
# Функция redactor отвечает за рендер шаблона редактора со всеми формами
# Функция createannouncement Создает объявление
# Функция editor отвечает за рендер шаблона эдитора объявлений со всеми формами и прошлыми данными
# Функция editannouncement отвечает за редактирование объявления
# Функция search отвечает за поиск
def index(request, anid=None):
def index(request):
group = None
superuser = False
ann_list = []
anns = Announcement.objects.all()
if request.method == 'GET' and anid != None:
for ann in range(anid-20, anid):
try:
ann_list.append(anns[ann])
except IndexError:
break
else:
for ann in range(anns.count()):
ann_list.append(anns[ann])
paginator = Paginator(anns, 20) # Сколько объявлений на странице
page_number = request.GET.get('page')
page_announcements = paginator.get_page(page_number)
if request.user.groups.exists():
group = request.user.groups.all()[0].name
if group in ['Teacher', 'admin']:
superuser = True
data = {'superuser': superuser, 'announcements': ann_list}
data = {'superuser': superuser, 'page_announcements': page_announcements, 'count': anns.count()}
return render(request, 'dec/dec.html', context=data)
#@allowed_users(allowed_roles=['Teacher', 'admin'])
@allowed_users(allowed_roles=['Учитель', 'Администратор'])
def redactor(request):
"""Отвечает за рендер шаблона редактора со всеми формами"""
covers_dir = settings.BASE_DIR / "media" / "covers"
covers = []
for cover in os.listdir(covers_dir):
covers.append("/media/covers/" + cover)
form = AnnouncementForm()
return render(request, "dec/red.html", context={'form': form})
return render(request, "dec/red.html", context={'form': form, 'covers': covers})
#@allowed_users(allowed_roles=['Teacher', 'admin'])
@allowed_users(allowed_roles=['Учитель', 'Администратор'])
def createannouncement(request):
"""Создает объявление (Записывает в БД)"""
if request.method != "POST":
return HttpResponsePermanentRedirect("/announcements")
form = AnnouncementForm(request.POST)
if form.is_valid():
pass
else:
return render(request, 'WrongData.html')
title = request.POST.get("title")
body = request.POST.get("body")
is_pinned = request.POST.get("is_pinned")
......@@ -77,8 +81,10 @@ def createannouncement(request):
return HttpResponsePermanentRedirect('/announcements')
#@allowed_users(allowed_roles=['Teacher', 'admin'])
@allowed_users(allowed_roles=['Учитель', 'Администратор'])
def editor(request, id):
"""Отвечает за рендер шаблона эдитора объявлений со всеми формами и прошлыми данными"""
try:
announcement = Announcement.objects.get(id=id)
initial_data = {
......@@ -89,12 +95,19 @@ def editor(request, id):
'image_url': announcement.image_url,
}
covers_dir = settings.BASE_DIR / "media" / "covers"
covers = []
for cover in os.listdir(covers_dir):
covers.append("/media/covers/" + cover)
form = AnnouncementForm(initial=initial_data)
data = {
'form': form,
'announcement_id': id,
'announcement': announcement,
'covers': covers
}
return render(request, 'dec/ed.html', context=data)
......@@ -103,8 +116,9 @@ def editor(request, id):
return render(request, 'WrongData.html')
#@allowed_users(allowed_roles=['Teacher', 'admin'])
@allowed_users(allowed_roles=['Учитель', 'Администратор'])
def editannouncement(request, id):
"""Отвечает за редактирование объявления (изменение существующих значений в БД)"""
try:
......@@ -118,13 +132,20 @@ def editannouncement(request, id):
is_pinned = request.POST.get("is_pinned", False)
if is_pinned : is_pinned = True
files_to_add = request.FILES.getlist('files')
files_to_delete = request.POST.getlist('file_id_to_delete[]')
image_url = request.POST.get('image_url')
files_to_delete = request.POST.get('file_id_to_delete')
image_url = request.POST.get('image_url')
for file_id in files_to_delete:
file = File.objects.get(pk=int(file_id))
file.file.delete()
file.delete()
if int(files_to_delete) is not -1:
if ',' in files_to_delete:
files_to_delete = files_to_delete.split(',')
else:
files_to_delete = [files_to_delete]
for file_id in files_to_delete:
file = File.objects.get(pk=int(file_id))
file.file.delete()
file.delete()
for file in files_to_add:
File.objects.create(announcement=announcement, file=file)
......@@ -146,47 +167,28 @@ def editannouncement(request, id):
return render(request, 'WrongData.html')
def search(request, anid=None):
def search(request):
"""Отвечает за функциональную часть поиска"""
query = request.GET.get('q')
ann_list = []
if query:
query_words = query.split()
query_filter = Q()
query_words = query.split()
query_filter = Q()
for word in query_words:
query_filter |= Q(title__icontains=word) | Q(body__icontains=word) | Q(author__first_name__icontains=word) | Q(author__last_name__icontains=word)
for word in query_words:
query_filter |= Q(title__icontains=word) | Q(body__icontains=word) | Q(
author__first_name__icontains=word) | Q(author__last_name__icontains=word)
anns = Announcement.objects.filter(query_filter)
anns = Announcement.objects.filter(query_filter)
if request.method == 'GET' and anid != None:
for ann in range(anid - 20, anid):
try:
ann_list.append(anns[ann])
except IndexError:
break
else:
for ann in range(anns.count()):
ann_list.append(anns[ann])
else:
query_words = query.split()
query_filter = Q()
for word in query_words:
query_filter |= Q(title__icontains=word) | Q(body__icontains=word) | Q(
author__first_name__icontains=word) | Q(author__last_name__icontains=word)
paginator = Paginator(anns, 20) # Сколько объявлений на странице
page_number = request.GET.get('page')
page_announcements = paginator.get_page(page_number)
anns = Announcement.objects.filter(query_filter)
for ann in range(anns.count()):
ann_list.append(anns[ann])
context = {
'announcements': ann_list,
data = {
'page_announcements': page_announcements,
'search_value': query,
}
return render(request, 'dec/dec.html', context=context)
return render(request, 'dec/dec.html', context=data)
def announcement(request, id):
......@@ -200,4 +202,24 @@ def announcement(request, id):
'announcement': announcement,
}
return render(request, 'dec/ann.html', context=context)
\ Нет новой строки в конце файла
return render(request, 'dec/ann.html', context=context)
@allowed_users(allowed_roles=['Учитель', 'Администратор'])
def delete_announcement(request, id):
"""Удаляет просроченные объявления и связанные с ними файлы"""
if request.method != 'GET':
return render(request, 'WrongData.html')
announcement = Announcement.objects.get(id=id)
files = announcement.files.all()
for file in files:
file.file.delete()
file.delete()
announcement.delete()
return HttpResponsePermanentRedirect('/announcements')
from django import forms
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from django.contrib import messages
User = get_user_model()
......
......@@ -19,8 +19,8 @@ class User(AbstractUser):
def set_full_name(self):
self.full_Name = self.last_name + self.first_name + self.middle_name
def save(self, *args, **kwargs):
def save_photo(self, *args, **kwargs):
super().save()
img = Image.open(self.avatar.path)
width, height = img.size # Get dimensions
......
......@@ -3,7 +3,7 @@ from .views import register, admin_menu, token_page, profile
urlpatterns = [
path('', include('django.contrib.auth.urls'), name='management'),
path('register/', register, name='register'),
path('register/<str:token>/', register, name='register'),
path('token_page/', token_page, name='token_page'),
path('admin_menu/', admin_menu, name='admin_menu'),
path('profile/', profile, name='profile'),
......
import string
import random
import time
import os
from dataclasses import dataclass, field
from django.contrib.auth import authenticate, login
......@@ -12,7 +14,12 @@ from .models import User, Tokens
from projects.models import Project
from django.core.paginator import Paginator, EmptyPage
def register(request):
def register(request, token):
temp_token = False
if Tokens.objects.filter(token=token).exists():
temp_token = True
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
......@@ -23,17 +30,21 @@ def register(request):
messages.success(request, 'Аккаунт успешно создан')
authenticate_user = authenticate(request, username=new_user.username, password=user_form.cleaned_data['password'])
login(request, authenticate_user)
if temp_token:
Tokens.objects.get(token=token).delete()
return redirect('profile')
elif request.POST.get('register_submit') and not user_form.is_valid():
messages.error(request, 'Что-то введено неправильно, проверьте пароли. Так же возможно такой логин или мейл уже существует!')
else:
user_form = UserRegistrationForm()
return render(request, 'registration/register.html', {'user_form': user_form})
return render(request, 'registration/register.html', {'user_form': user_form, "is_token": temp_token, "token": token})
def token_page(request):
if request.method == "POST":
token = request.POST.get("token")
if Tokens.objects.filter(token=token).exists():
temp_token = Tokens.objects.get(token=token).delete()
return redirect('register')
return redirect(f'/management/register/{token}/')
else:
messages.error(request, 'Неправильный токен!')
......@@ -160,6 +171,7 @@ def admin_menu(request):
user.is_superuser = True
elif role == "Учитель":
user.group = 0
user.is_superuser = False
else:
user.is_superuser = False
......@@ -213,7 +225,6 @@ def profile(request):
request_projects_packs = [ProjectsPack(project) for project in request_projects_T]
user_full = request.user.getPName()
if request.method == "POST":
new_password = request.POST.get('new_password')
old_password = request.POST.get('old_password')
......@@ -243,8 +254,9 @@ def profile(request):
if request.POST.get('avatar_submit') and request.FILES:
os.remove(request.user.avatar.path, dir_fd=None)
request.user.avatar = new_avatar
request.user.save()
request.user.save_photo()
return redirect('profile')
context = {"open_projects_packs": open_projects_packs,
......
......@@ -110,13 +110,14 @@ def index(request: HttpRequest):
# отправка страницы с формой для подачи заявки на проект
def send_create_form(request: HttpRequest, context_theme={}):
def send_create_form(request: HttpRequest, context_data={}):
print(context_data)
if request.user.is_authenticated:
if request.user.role == "Ученик":
teachers = User.objects.filter(role="Учитель", is_other_teacher=True)
data = {"teachers": teachers,
"subjects_names": [subject.name for subject in Subject.objects.all()]}
data.update(context_theme)
data.update(context_data)
return render(request, "projects/create.html", data)
return render(request, "NotEnoughPermissions.html")
......@@ -148,12 +149,15 @@ def create(request: HttpRequest):
subject = request.POST.get("subject")
name = request.POST.get("name")
is_another_teacher = request.POST.get('teacher-checkbox')
description = request.POST.get('description', '')
try:
if is_another_teacher == 'on': # если учитель не из лицея
another_teacher = request.POST.get("new-teacher", -1)
if another_teacher == -1:
messages.error(request, 'Неверно введённые данные')
return render(request, "projects/create.html")
return send_create_form(request, context_data={'name': name, 'description': description})
last_user = User.objects.last()
if last_user is None:
new_id = 1
......@@ -168,11 +172,12 @@ def create(request: HttpRequest):
else:
if teacher_id == -1:
messages.error(request, 'Неверно введённые данные')
return render(request, "projects/create.html")
print({'name': name, 'description': description})
return send_create_form(request, context_data={'name': name, 'description': description})
teacher = User.objects.get(id=teacher_id)
if teacher.role != "Учитель":
return render(request, "WrongData.html")
description = request.POST.get('description', '')
return send_create_form(request, context_data={'name': name, 'description': description})
project = Project.objects.create(name=name, teacher=teacher, student=request.user)
if description != -1:
project.description = description
......@@ -186,7 +191,8 @@ def create(request: HttpRequest):
return render(request, "projects/success.html")
except User.DoesNotExist: # если не удалось получить пользователя из бд
return render(request, "WrongData.html")
except BaseException: # если возникла непредвиденная ошибка
except BaseException as e: # если возникла непредвиденная ошибка
print(e)
return render(request, "FatalError.html")
......
html {
box-sizing: border-box;
width: 100%;
height: 100%;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
a {
color: inherit;
text-decoration: none;
}
img {
max-width: 100%;
}
body {
margin: 0;
font-family: 'Roboto', Verdana, sans-serif;
min-width: 100%;
min-height: 100%;
background-color: #E8F1FF;
}
.main-nav {
position: relative;
display: block;
width: 60%;
width: 80%;
margin: 0 auto;
}
......@@ -39,45 +10,19 @@ body {
margin: 0 auto;
margin-top: 100px;
width: 80%;
margin-left: 10%;
overflow: scroll;
/*убираем полосу прокрутки*/
-ms-overflow-style: none;/* IE and Edge */
scrollbar-width: none;/* Firefox */
}
/*Настройки скроллбара в Chrome*/
.main::-webkit-scrollbar {
display: none;
/* Hide scrollbar for Chrome, Safari and Opera */
}
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: 5px 5px 5px -5px rgba(8, 42, 70, 0.2) inset;
background-color: #e8f1ff;
}
::-webkit-scrollbar-thumb {
background-color: rgb(4, 0, 80);
background-image: -webkit-linear-gradient(45deg,
rgba(4, 0, 80, 1) 0%,
rgba(9, 9, 121, 1) 31%,
rgba(0, 162, 195, 1) 100%);
display: flex;
flex-direction: column;
align-items: center;
}
.nav {
position: relative;
display: block;
width: 50%;
margin: 0 auto;
.search-form {
display: flex;
column-gap: 10px;
align-items: center;
margin-bottom: 20px;
}
.search {
margin-bottom: 20px;
width: 100%;
display: flex;
justify-content: center;
......@@ -109,212 +54,261 @@ body {
}
.decorate {
background-color: #E8F1FF;
width: 14em;
height: 3em;
border-radius: 10px;
text-align: center;
display: grid;
margin: auto;
z-index: 5;
.create-link {
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
white-space: nowrap;
font-size: 16px;
border-radius: 25px;
cursor: pointer;
background: #103A84;
color: #fff;
transition: background 0.3s linear;
}
.Adecorate {
display: block;
width: 100%;
height: 3em;
z-index: 10;
grid-row:1;
grid-column: 1;
.create-link:hover {
overflow: visible;
background: #0d306d;
transition: background 0.3s linear;
}
.Tdecorate {
align-self: center;
z-index: 0;
grid-row: 1;
grid-column: 1;
.create-link:active {
background: #3e5e96;
}
.Tdecorate:hover,.Tdecorate.hover {
cursor: pointer;
.create-link > span {
padding-right: 0px;
max-width: 0;
overflow: hidden;
transition: max-width 0.3s cubic-bezier(0, 1, 0, 1);
}
.decorate:hover,.decorate.hover {
background-color: white;
.create-link:hover > span {
padding-right: 5px;
max-width: 500px;
transition: max-width 0.3s cubic-bezier(1, 0, 1, 0);
}
/* FSL settings */
.FSL {
background-color: #E8F1FF;
.content {
width: 100%;
}
/* grid-area: o; */
/* grid-auto-rows: auto; */
.announ {
display: flex;
/* flex-flow: column wrap; */
flex-flow: row wrap;
justify-content: space-around;
/* background: #FFFFFF; */
/* border: 1px solid #999999; */
border-radius: 9px;
max-width: 1000px;
margin: 0 auto;
}
.flex {
display: grid;
grid-template-columns: repeat(5,20%);
grid-template-rows: repeat(20,5%);
width: 45%;
min-height: 150px;
margin: 2%;
max-height: 250px;
background: #FFFFFF;
/* Back shadow */
box-shadow: 0px 0px 4px 3px rgba(63, 63, 63, 0.55);
border-radius: 9px;
.announ:not(:last-child) {
margin-bottom: 20px;
}
.IB {
background-color: #051A3F;
height: 100%;
width: 100%;
border-radius: 0 9px 9px 0;
grid-area: 1/5/21/6;
.announ-content {
padding: 16px 27px;
border-top-left-radius: 9px;
border-bottom-left-radius: 9px;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
width: 750px;
min-height: 250px;
background: #fff;
}
.cap{
grid-area: 2/1/3/5;
padding-bottom: 1%;
font-size: 32px;
margin-top: 0;
padding-left: 4%;
font-weight: 700;
.top-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.text{
grid-area: 3/1/19/5;
padding-top: 10px;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 4%;
max-height: 9.5em;
.top-info-text {
color: #A3A4A6;
font-size: 15px;
font-style: normal;
}
@media (max-width: 2000px){
/* стили для xs-устройств */
.flex {
width: 80%;
min-height: 9em;
max-height: 15em;
margin: 2%;
border-radius: 8px;
}
.Fother {
justify-content: center;
}
.IB{
border-radius: 0 8px 8px 0;
}
.favourite-btn {
background: none;
border: none;
outline: none;
width: 20px;
height: 20px;
cursor: pointer;
fill: #DBDBDB;
}
ul {
list-style: none;
margin: 0;
padding: 0;
.favourite-svg > path {
transition: fill .3s linear;
}
.btn {
border: none;
outline: none;
background: none;
cursor: pointer;
.favourite-btn:hover {
fill: #bdbdbd;
}
.dec {
position: relative;
width: 600px;
background: #fff;
border-radius: 30px;
.middle-info {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
padding: 0 30px;
align-items: flex-start;
}
.btn-dec {
display: block;
width: 52em;
.middle-info > a {
border-radius: 5px;
transition: background 0.3s linear;
}
.btn-dec::after {
content: "";
position: absolute;
z-index: 2;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 30px;
.announ-title {
margin: 0;
margin-bottom: 10px;
color: #000;
font-size: 36px;
font-weight: 700;
line-height: normal;
}
.dec-list {
width: 100%;
transition: max-height 0.3s cubic-bezier(0, 1, 0, 1);
.announ-descr {
margin: 0;
margin-bottom: 10px;
color: #000;
font-size: 15px;
font-weight: 300;
line-height: normal;
overflow: hidden;
max-height: 0;
}
.ul-open {
.announ-descr-close {
max-height: 250px;
transition: max-height 0.3s cubic-bezier(0, 1, 0, 1);
}
.announ-descr-open {
max-height: 3000px;
transition: max-height 0.3s cubic-bezier(1, 0, 1, 0);
max-height: 2000px;
position: relative;
z-index: 3;
}
.dec-item {
padding: 10px 10px;
.announ-btn {
position: absolute;
z-index: -10;
padding: 0;
margin-bottom: 20px;
background: none;
border: none;
outline: none;
color: #000;
font-size: 15px;
font-weight: 700;
line-height: normal;
cursor: pointer;
align-self: flex-start;
opacity: 0;
}
.announ-btn-active {
position: relative;
background: url('../../img/profile/arrow-right.svg') no-repeat center right;
background-size: 30px 30px;
border-radius: 5px;
transition: 0.3s background-color;
z-index: 1;
opacity: 1;
}
.dec-item:last-child {
margin-bottom: 20px;
.announ-bottom {
display: flex;
align-items: flex-start;
/* justify-content: flex-start; */
}
.dec-item:hover {
background-color: #ebebeb;
.announ-author {
display: flex;
flex-wrap: nowrap;
margin-right: 20px;
font-weight: 400;
font-size: 15px;
line-height: 20px;
color: #5A88FF;
}
.dec-item:active {
background-color: #fff;
.announ-files-list {
max-width: 80%;
list-style: none;
display: flex;
flex-wrap: wrap;
row-gap: 10px;
column-gap: 20px;
}
.dec-name {
padding: 0;
font-size: 18px;
display: block;
.announ-files-item {
position: relative;
padding: 6px 10px;
border: 1px solid #103A84;
border-radius: 3px;
}
.announ-files-link {
color: #103A84;
}
.dec-name::after {
.announ-files-link::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
left: 0;
}
.dec-span {
font-size: 15px;
font-weight: 300;
.announ-img {
overflow: hidden;
width: calc(100% - 750px);
background: #103A84;
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
border-top-right-radius: 9px;
border-bottom-right-radius: 9px;
}
.load-btn {
padding: 10px 30px;
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
border: none;
outline: none;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
background: #103A84;
color: #fff;
transition: background 0.3s linear;
}
.dec-span:not(:last-child) {
margin-right: 2px;
.load-btn:hover {
background: #0d306d;
}
.load-btn:active {
background: #3e5e96;
}
@media (max-width: 2000px){
/* стили для xs-устройств */
.flex {
width: 80%;
min-height: 9em;
max-height: 15em;
margin: 2%;
border-radius: 8px;
}
.Fother {
justify-content: center;
}
.IB{
border-radius: 0 8px 8px 0;
}
}
\ Нет новой строки в конце файла
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать