388 lines
19 KiB
Python
388 lines
19 KiB
Python
import flet as ft
|
||
|
||
# Глобальный масштаб интерфейса. 1.0 = 100%, 1.7 = 170%.
|
||
# Меняйте только эту переменную (или через theme_manager.apply_scale),
|
||
# чтобы масштабировать весь UI.
|
||
SCALE: float = 1.7
|
||
|
||
|
||
def s(v: int | float) -> int:
|
||
"""Применяет SCALE к любому размеру: шрифтам, отступам, ицих."""
|
||
return round(v * SCALE)
|
||
|
||
|
||
class colors:
|
||
# Основная палитра в стиле copp74.ru
|
||
primary = "#F85A40" # Основной оранжево-красный (логотип ЦОПП)
|
||
primary_dark = "#D94530" # Тёмный для hover/активных состояний
|
||
primary_light = "#FFEAB7" # Светлый персиковый (вторичный фон)
|
||
|
||
# Фоны
|
||
background = "#F4F6F8" # Светло-серый фон страницы
|
||
surface = "#FFFFFF" # Белый для карточек и форм
|
||
surface_alt = "#F0F2F5" # Альтернативные блоки
|
||
|
||
# Акцент — жёлтый
|
||
accent = "#FFC845" # Жёлтый акцент
|
||
accent_lime = "#FFC845" # Алиас для совместимости
|
||
secondary_light = "#FFEAB7" # Алиас для совместимости
|
||
secondary_yellow = "#FFC845" # Алиас
|
||
|
||
# Синий — информация, ссылки
|
||
info = "#007FBD" # Синий
|
||
info_light = "#D9F1F4" # Светлый голубой
|
||
tertiary_light = "#D9F1F4" # Алиас для совместимости
|
||
tertiary_blue = "#007FBD" # Алиас для совместимости
|
||
secondary_orange = "#F85A40" # Алиас для совместимости
|
||
|
||
# Зелёный — успех
|
||
success = "#85C446" # Зелёный
|
||
success_light = "#C0DE9E" # Светло-зелёный
|
||
|
||
# Текст
|
||
text_primary = "#1E2A3A" # Почти чёрный для заголовков
|
||
text_secondary = "#5A6779" # Серый для второстепенного текста
|
||
text_on_primary = "#FFFFFF" # Белый текст на оранжевом фоне
|
||
|
||
# Граница
|
||
border = "#DDE3EA" # Светло-серая граница
|
||
|
||
class CastomText(ft.Text):
|
||
def __init__(self, text, size=14, color=colors.text_primary, weight=ft.FontWeight.NORMAL, **kwargs):
|
||
super().__init__(
|
||
value=text,
|
||
color=color,
|
||
size=s(size),
|
||
weight=weight,
|
||
max_lines=99,
|
||
**kwargs,
|
||
)
|
||
|
||
|
||
class CastomTextField_input(ft.TextField):
|
||
def __init__(self, on_change=None, on_submit=None, label="Ваше имя", hint_text="Введите текст...", prefix_icon=None, suffix_icon=None, **kwargs):
|
||
super().__init__(
|
||
label=label,
|
||
hint_text=hint_text,
|
||
filled=True,
|
||
bgcolor=colors.surface,
|
||
border_color=colors.border,
|
||
focused_border_color=colors.primary,
|
||
border_radius=s(10),
|
||
label_style=ft.TextStyle(
|
||
color=colors.text_secondary,
|
||
size=s(13),
|
||
weight=ft.FontWeight.W_500,
|
||
),
|
||
hint_style=ft.TextStyle(
|
||
color=colors.border,
|
||
size=s(14),
|
||
italic=True,
|
||
),
|
||
text_style=ft.TextStyle(
|
||
color=colors.text_primary,
|
||
size=s(15),
|
||
),
|
||
content_padding=ft.padding.symmetric(horizontal=s(14), vertical=s(14)),
|
||
prefix_icon=prefix_icon,
|
||
suffix_icon=suffix_icon,
|
||
on_change=on_change,
|
||
on_submit=on_submit,
|
||
**kwargs,
|
||
)
|
||
|
||
class CastomButton(ft.Button):
|
||
def __init__(self, text, on_click):
|
||
super().__init__(
|
||
content=ft.Text(
|
||
text,
|
||
color=colors.text_on_primary,
|
||
size=s(14),
|
||
weight=ft.FontWeight.W_600,
|
||
) if isinstance(text, str) else text,
|
||
style=ft.ButtonStyle(
|
||
color=colors.text_on_primary,
|
||
bgcolor={
|
||
ft.ControlState.DEFAULT: colors.primary,
|
||
ft.ControlState.HOVERED: colors.primary_dark,
|
||
},
|
||
shape=ft.RoundedRectangleBorder(radius=s(10)),
|
||
padding=ft.padding.symmetric(horizontal=s(20), vertical=s(12)),
|
||
),
|
||
on_click=on_click
|
||
)
|
||
|
||
|
||
class CastomIconButton(ft.Button):
|
||
"""Кнопка с иконкой и текстом."""
|
||
def __init__(self, text, icon, on_click):
|
||
super().__init__(
|
||
content=ft.Row(
|
||
spacing=s(8),
|
||
tight=True,
|
||
controls=[
|
||
ft.Icon(icon, color=colors.text_on_primary, size=s(18)),
|
||
ft.Text(text, color=colors.text_on_primary, size=s(14), weight=ft.FontWeight.W_600),
|
||
],
|
||
),
|
||
style=ft.ButtonStyle(
|
||
bgcolor={
|
||
ft.ControlState.DEFAULT: colors.info,
|
||
ft.ControlState.HOVERED: "#006AA3",
|
||
},
|
||
shape=ft.RoundedRectangleBorder(radius=s(10)),
|
||
padding=ft.padding.symmetric(horizontal=s(18), vertical=s(12)),
|
||
),
|
||
on_click=on_click,
|
||
)
|
||
|
||
class CastomDropdown(ft.Dropdown):
|
||
def __init__(self, label="", hint_text="", options=None, on_change=None):
|
||
super().__init__(
|
||
label=label,
|
||
hint_text=hint_text,
|
||
options=options or [],
|
||
filled=True,
|
||
fill_color=colors.surface,
|
||
color=colors.text_primary,
|
||
border_color=colors.border,
|
||
border_radius=s(10),
|
||
focused_border_color=colors.primary,
|
||
label_style=ft.TextStyle(
|
||
color=colors.text_secondary,
|
||
size=s(13),
|
||
weight=ft.FontWeight.W_500,
|
||
),
|
||
hint_style=ft.TextStyle(
|
||
color=colors.border,
|
||
size=s(14),
|
||
italic=True,
|
||
),
|
||
text_style=ft.TextStyle(
|
||
color=colors.text_primary,
|
||
size=s(15),
|
||
),
|
||
menu_style=ft.MenuStyle(
|
||
bgcolor=colors.surface,
|
||
),
|
||
)
|
||
if on_change is not None:
|
||
self.on_select = on_change
|
||
|
||
|
||
class CastomSwitch(ft.Switch):
|
||
def __init__(self, label, on_change):
|
||
super().__init__(
|
||
label=label,
|
||
label_text_style=ft.TextStyle(size=s(13), color=colors.text_primary),
|
||
active_color=colors.primary,
|
||
active_track_color=colors.primary_light,
|
||
inactive_thumb_color=colors.border,
|
||
inactive_track_color=colors.surface_alt,
|
||
on_change=on_change
|
||
)
|
||
|
||
|
||
class AlterDiaalogApproval(ft.AlertDialog):
|
||
def __init__(self, on_accept=None):
|
||
self._on_accept = on_accept
|
||
|
||
self.switch_approval = CastomSwitch(
|
||
label="",
|
||
on_change=self.switch_changed
|
||
)
|
||
|
||
self.button_approval = CastomButton(
|
||
text="Принять",
|
||
on_click=self.accept
|
||
)
|
||
self.button_approval.disabled = True
|
||
|
||
super().__init__(
|
||
bgcolor=colors.surface,
|
||
title=ft.Text(
|
||
"Пользовательское соглашение",
|
||
color=colors.primary,
|
||
size=s(16),
|
||
weight=ft.FontWeight.BOLD,
|
||
),
|
||
content=ft.Container(
|
||
height=s(400),
|
||
content=ft.Column(
|
||
scroll=ft.ScrollMode.AUTO,
|
||
expand=True,
|
||
controls=[
|
||
ft.Markdown(
|
||
"""
|
||
# ПОЛЬЗОВАТЕЛЬСКОЕ СОГЛАШЕНИЕ
|
||
|
||
## 1. ОБЩИЕ ПОЛОЖЕНИЯ
|
||
|
||
Настоящее Пользовательское соглашение (далее — «Соглашение») регулирует отношения между администрацией сайта https://copp74.ru (далее — «Сайт», «Мы», «Администрация») и пользователем (далее — «Пользователь», «Вы»).
|
||
|
||
Используя Сайт, Вы автоматически соглашаетесь со всеми условиями настоящего Соглашения. Если Вы не согласны с этими условиями, пожалуйста, не используйте Сайт.
|
||
|
||
## 2. ОПИСАНИЕ САЙТА
|
||
|
||
Сайт https://copp74.ru представляет собой онлайн-платформу для прохождения тестов и проверки знаний. Основная функция Сайта — предоставление Пользователям возможности проходить различные тесты и получать результаты.
|
||
|
||
## 3. РЕГИСТРАЦИЯ И ЛИЧНЫЕ ДАННЫЕ
|
||
|
||
### 3.1 Регистрация
|
||
|
||
Для использования Сайта Пользователь должен зарегистрироваться и указать следующую информацию:
|
||
- Фамилию
|
||
- Имя
|
||
- Учебное заведение (образовательную организацию)
|
||
|
||
### 3.2 Назначение личных данных
|
||
|
||
Указанные фамилия, имя и учебное заведение используются исключительно для:
|
||
- Идентификации Пользователя в системе
|
||
- Отображения результатов тестирования
|
||
- Ведения статистики прохождения тестов
|
||
- Организации пользователей по учебным заведениям для аналитики
|
||
- Внутреннего анализа и улучшения качества тестирования
|
||
|
||
### 3.3 Защита личных данных
|
||
|
||
**Мы гарантируем, что Ваши персональные данные (фамилия, имя и информация об учебном заведении):**
|
||
- Не будут переданы третьим лицам или организациям без Вашего согласия
|
||
- Не будут использованы в коммерческих целях
|
||
- Не будут распространяться без Вашего согласия
|
||
- Хранятся в защищенной базе данных
|
||
|
||
Данные будут удалены в соответствии с законодательством Российской Федерации по защите персональных данных при закрытии аккаунта или по запросу Пользователя.
|
||
|
||
## 4. ПРАВА И ОБЯЗАННОСТИ ПОЛЬЗОВАТЕЛЯ
|
||
|
||
### 4.1 Права Пользователя
|
||
|
||
Пользователь имеет право:
|
||
- Проходить тесты, размещенные на Сайте
|
||
- Получать результаты своего тестирования
|
||
- Запросить удаление своего аккаунта и данных
|
||
- Обратиться в поддержку по вопросам использования Сайта
|
||
- Получить информацию о том, как его данные обрабатываются
|
||
|
||
### 4.2 Обязанности Пользователя
|
||
|
||
Пользователь обязуется:
|
||
- Предоставлять достоверную информацию при регистрации
|
||
- Указывать корректное название учебного заведения
|
||
- Не использовать Сайт в противоправных целях
|
||
- Не пытаться получить несанкционированный доступ к системам Сайта
|
||
- Не воспроизводить, не копировать и не распространять содержимое Сайта без разрешения
|
||
- Соблюдать все применимые законы Российской Федерации
|
||
|
||
## 5. ОГРАНИЧЕНИЕ ОТВЕТСТВЕННОСТИ
|
||
|
||
### 5.1 Предоставление Сайта «Как есть»
|
||
|
||
Сайт предоставляется в режиме «как есть» без каких-либо явных или подразумеваемых гарантий. Администрация не гарантирует:
|
||
- Непрерывную работу Сайта
|
||
- Отсутствие ошибок в тестах или результатах
|
||
- Соответствие Сайта конкретным целям Пользователя
|
||
|
||
### 5.2 Ограничение ответственности
|
||
|
||
В максимальной степени, допускаемой законодательством Российской Федерации, Администрация не несет ответственность за:
|
||
- Убытки, возникшие в результате использования или невозможности использования Сайта
|
||
- Технические сбои, потерю данных
|
||
- Ущерб, причиненный прямо или косвенно в связи с использованием Сайта
|
||
|
||
## 6. ИНТЕЛЛЕКТУАЛЬНАЯ СОБСТВЕННОСТЬ
|
||
|
||
Все содержимое Сайта, включая тесты, вопросы, задания, дизайн, логотипы и тексты, защищено авторским правом и законами об интеллектуальной собственности Российской Федерации. Пользователь не имеет права использовать это содержимое в коммерческих целях без письменного разрешения Администрации.
|
||
|
||
## 7. ИЗМЕНЕНИЕ СОГЛАШЕНИЯ
|
||
|
||
Администрация оставляет за собой право изменять условия настоящего Соглашения в любое время. Изменения вступают в силе с момента их публикации на Сайте. Продолжение использования Сайта после публикации изменений означает согласие Пользователя с новыми условиями.
|
||
|
||
## 8. ПРЕКРАЩЕНИЕ ДОСТУПА
|
||
|
||
Администрация может заблокировать доступ Пользователя к Сайту без предварительного уведомления, если:
|
||
- Пользователь нарушает условия настоящего Соглашения
|
||
- Пользователь использует Сайт в противоправных целях
|
||
- Пользователь предоставил поддельную информацию при регистрации
|
||
|
||
## 9. КОНФИДЕНЦИАЛЬНОСТЬ
|
||
|
||
Пожалуйста, ознакомьтесь с нашей Политикой конфиденциальности, которая описывает, как мы обращаемся с Вашими личными данными и какие меры безопасности мы применяем.
|
||
|
||
## 10. ПРИМЕНИМОЕ ПРАВО
|
||
|
||
Настоящее Соглашение регулируется законодательством Российской Федерации. Все споры, возникающие в связи с Соглашением, подлежат разрешению в судах Российской Федерации.
|
||
|
||
## 11. КОНТАКТНАЯ ИНФОРМАЦИЯ
|
||
|
||
Если у Вас есть вопросы по данному Соглашению или функционированию Сайта https://copp74.ru, свяжитесь с нами через форму обратной связи на Сайте.
|
||
|
||
## 12. ЗАКЛЮЧИТЕЛЬНЫЕ ПОЛОЖЕНИЯ
|
||
|
||
Если какая-либо часть настоящего Соглашения будет признана недействительной, остальные положения остаются в полной силе.
|
||
|
||
Пользователь подтверждает, что прочитал, понял и согласен со всеми условиями настоящего Соглашения перед использованием Сайта.
|
||
|
||
**Дата последнего обновления:** 19.03.2026
|
||
|
||
---
|
||
|
||
*Соглашение соответствует законодательству Российской Федерации, включая Федеральный закон «О защите персональных данных» и Федеральный закон «О защите прав потребителей».*
|
||
""",
|
||
expand=True,
|
||
md_style_sheet=ft.MarkdownStyleSheet(
|
||
p_text_style=ft.TextStyle(color=colors.text_primary, size=s(12)),
|
||
h1_text_style=ft.TextStyle(color=colors.primary, size=s(18), weight=ft.FontWeight.BOLD),
|
||
h2_text_style=ft.TextStyle(color=colors.info, size=s(15), weight=ft.FontWeight.BOLD),
|
||
h3_text_style=ft.TextStyle(color=colors.text_secondary, size=s(13), weight=ft.FontWeight.BOLD),
|
||
a_text_style=ft.TextStyle(color=colors.primary),
|
||
),
|
||
)
|
||
]
|
||
),
|
||
),
|
||
actions=[
|
||
ft.Column(
|
||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||
controls=[
|
||
ft.Row(
|
||
expand=True,
|
||
alignment=ft.MainAxisAlignment.START,
|
||
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
||
controls=[
|
||
self.switch_approval,
|
||
ft.Text(
|
||
"Я согласен с условиями пользовательского соглашения",
|
||
size=s(13),
|
||
color=colors.text_primary,
|
||
expand=True,
|
||
no_wrap=False,
|
||
),
|
||
]
|
||
),
|
||
ft.Row(
|
||
expand=True,
|
||
alignment=ft.MainAxisAlignment.CENTER,
|
||
controls=[
|
||
self.button_approval,
|
||
]
|
||
)
|
||
]
|
||
)
|
||
],
|
||
actions_alignment=ft.MainAxisAlignment.CENTER,
|
||
)
|
||
|
||
def switch_changed(self, e):
|
||
#self.switch_approval.value = not e.control.value
|
||
self.button_approval.disabled = not e.control.value
|
||
self.update()
|
||
|
||
def accept(self, e):
|
||
self.open = False
|
||
self.page.update()
|
||
if self._on_accept:
|
||
self._on_accept()
|
||
else:
|
||
self.page.run_task(self.page.push_route, "/reg") |