update admin panel and visual

This commit is contained in:
greyjoy
2026-08-02 22:56:29 +04:00
parent b53aa305e8
commit 024e28eee4
16 changed files with 866 additions and 309 deletions
@@ -0,0 +1,15 @@
{% extends "admin/base.html" %}
{% load i18n static %}
{% block title %}{{ title }} | Кабинет преподавателя{% endblock %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'admin/css/teacher_admin.css' %}">
{% endblock %}
{% block branding %}
<div id="site-name"><a href="{% url 'teacher_admin:index' %}"><span>ИО</span> Кабинет преподавателя</a></div>
{% endblock %}
{% block nav-global %}<a class="admin-site-link" href="/" target="_blank" rel="noopener">Открыть сайт ↗</a>{% endblock %}
+24
View File
@@ -0,0 +1,24 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block content %}
<div class="dashboard">
<section class="dashboard-hero">
<div><p>Рабочий обзор</p><h1>Добрый день, {{ user.get_short_name|default:user.get_username }}.</h1><span>Управляйте расписанием и заявками в одном месте.</span></div>
<a class="dashboard-primary" href="{% url 'teacher_admin:consultations_consultationslot_add' %}">+ Добавить свободное время</a>
</section>
<section class="dashboard-metrics">
<a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}?status__exact=new"><strong>{{ dashboard.new_requests }}</strong><span>новых заявок</span></a>
<a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}?status__exact=confirmed&amp;preferred_date__exact={{ today|date:'Y-m-d' }}"><strong>{{ dashboard.today_confirmed }}</strong><span>подтверждено на сегодня</span></a>
<a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}"><strong>{{ dashboard.upcoming }}</strong><span>ближайших консультаций</span></a>
<a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}?status__exact=completed"><strong>{{ dashboard.completed_month }}</strong><span>проведено за месяц</span></a>
</section>
<section class="dashboard-grid">
<div class="dashboard-panel"><div class="panel-heading"><h2>Ближайшие записи</h2><a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}">Все заявки →</a></div>
{% if next_bookings %}<div class="booking-list">{% for booking in next_bookings %}<a href="{% url 'teacher_admin:consultations_consultationrequest_change' booking.pk %}"><span class="booking-date">{{ booking.preferred_date|date:"d M" }}<small>{{ booking.preferred_time|time:"H:i" }}</small></span><span><strong>{{ booking.name }}</strong><small>{{ booking.get_subject_display }} · {{ booking.get_exam_display }}</small></span><span class="dashboard-status status-{{ booking.status }}">{{ booking.get_status_display }}</span></a>{% endfor %}</div>{% else %}<p class="empty-panel">Ближайших записей пока нет. Добавьте свободные окна в расписание.</p>{% endif %}
</div>
<div class="dashboard-panel quick-panel"><div class="panel-heading"><h2>Быстрые действия</h2></div><a href="{% url 'teacher_admin:consultations_consultationslot_changelist' %}">Календарь и свободные окна <span></span></a><a href="{% url 'teacher_admin:consultations_consultationrequest_changelist' %}?status__exact=new">Новые заявки <span></span></a><a href="{% url 'teacher_admin:consultations_consultationrequest_add' %}">Добавить запись вручную <span></span></a></div>
</section>
<section class="dashboard-apps">{% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}</section>
</div>
{% endblock %}
+133 -103
View File
@@ -4,191 +4,221 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Подготовка к ОГЭ и ЕГЭ по физике и математике с преподавателем Игорем Олеговичем Безрукавовым.">
<title>Физика и математика — Игорь Олегович Безрукавов</title>
<meta name="description" content="Подготовка к ОГЭ и ЕГЭ по физике и математике с Игорем Олеговичем Безрукавовым.">
<title>Игорь Безрукавов — физика и математика</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&amp;family=Source+Serif+4:opsz,wght@8..60,600;8..60,700&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=DM+Mono&amp;family=Manrope:wght@400;500;600;700;800&amp;family=Source+Serif+4:opsz,wght@8..60,600;8..60,700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<header class="site-header">
<div class="container navigation">
<a class="brand" href="#top" aria-label="На главную">ИО<span>·</span>учёба</a>
<a class="brand" href="#top" aria-label="На главную"><span>ИО</span> physics &amp; math</a>
<nav class="nav-links" aria-label="Основная навигация">
<a href="#about">О преподавателе</a>
<a href="#subjects">Направления</a>
<a href="#booking">Запись</a>
<a href="#programs">Направления</a>
<a href="#faq">Вопросы</a>
</nav>
<a class="nav-button" href="#booking">Записаться</a>
<a class="header-action" href="#booking">Выбрать время <span aria-hidden="true"></span></a>
</div>
</header>
<main id="top">
<section class="hero">
<div class="container hero-grid">
<div class="hero-content">
<p class="eyebrow">Физика и математика · 5–11 классы</p>
<h1>Готовимся к экзаменам <em>с пониманием</em>, а не наугад.</h1>
<p class="hero-text">Индивидуальные консультации по подготовке к ОГЭ и ЕГЭ: разбираем сложные темы, выстраиваем логику решения и спокойно идём к цели.</p>
<div class="container hero-layout">
<div class="hero-copy">
<p class="eyebrow"><span></span> Физика · математика · 5–11 классы</p>
<h1>Экзамен — это задача. <em>Её можно решить.</em></h1>
<p class="hero-lead">Индивидуальная подготовка к ОГЭ и ЕГЭ: от пробелов в темах до уверенности в каждом шаге решения.</p>
<div class="hero-actions">
<a class="button button-primary" href="#booking">Записаться на консультацию</a>
<a class="text-link" href="#about">Узнать обо мне <span aria-hidden="true"></span></a>
<a class="button button-primary" href="#booking">Записаться на консультацию <span aria-hidden="true"></span></a>
<a class="button button-ghost" href="#about">О преподавателе</a>
</div>
<div class="hero-stats" aria-label="Ключевые факты">
<div><strong>10+</strong><span>лет опыта</span></div>
<div><strong>2</strong><span>предмета</span></div>
<div><strong>ОГЭ · ЕГЭ</strong><span>подготовка к экзаменам</span></div>
<div class="hero-numbers" aria-label="Опыт преподавателя">
<div><strong>10+</strong><span>лет преподавания<br>и репетиторства</span></div>
<div><strong>2</strong><span>предмета для<br>уверенного результата</span></div>
<div><strong>АГУ</strong><span>бакалавриат<br>и магистратура</span></div>
</div>
</div>
<div class="formula-card" aria-label="Физика и математика">
<div class="card-orbit orbit-one"></div>
<div class="card-orbit orbit-two"></div>
<span class="formula formula-e">E = mc²</span>
<span class="formula formula-root">√x</span>
<span class="formula formula-sum">Σ</span>
<div class="formula-center">
<p>точные науки</p>
<strong>понятно<br>и спокойно</strong>
<div class="portrait-zone">
<div class="portrait-frame">
<img src="{% static 'images/igor-bezrukavov.jpg' %}" alt="Игорь Олегович Безрукавов">
</div>
<div class="portrait-note"><span></span> Разбираем<br>до понимания</div>
<div class="formula-sticker" aria-hidden="true"><i>F</i><small>= ma</small></div>
</div>
</div>
</section>
<section class="trust-strip">
<div class="container trust-content">
<span>Понятная система подготовки</span><b>·</b>
<span>Спокойный темп</span><b>·</b>
<span>Индивидуальный план</span><b>·</b>
<span>Фокус на результате</span>
</div>
</section>
<section class="section about" id="about">
<div class="container split-layout">
<p class="section-kicker">О преподавателе</p>
<div>
<h2>Игорь Олегович<br>Безрукавов</h2>
<p class="lead">Преподаватель физики и математики с опытом преподавания и репетиторства более 10 лет.</p>
<p>Окончил Астраханский государственный университет: бакалавриат и магистратуру по специальности «Преподавание физики и математики».</p>
<p>На консультации важно не просто получить ответ, а разобраться, почему решение работает — тогда знания остаются с учеником и на контрольной, и на экзамене.</p>
<div class="container about-layout">
<div class="section-label"><span>01</span><p>О преподавателе</p></div>
<div class="about-content">
<h2>Понимание формул начинается с вопроса <em>«почему?»</em></h2>
<div class="about-columns">
<p class="about-intro">Меня зовут Игорь Олегович Безрукавов. Более 10 лет помогаю школьникам выстроить сильную базу по физике и математике.</p>
<div>
<p>Окончил Астраханский государственный университет: бакалавриат и магистратуру по специальности «Преподавание физики и математики».</p>
<p>На занятии ученик не заучивает шаблон, а учится видеть логику задачи — это помогает уверенно работать и на уроке, и на экзамене.</p>
</div>
</div>
<div class="education-mark"><span>АГУ</span><p>Педагогическое образование<br>по физике и математике</p></div>
</div>
</div>
</section>
<section class="section subjects" id="subjects">
<section class="section programs" id="programs">
<div class="container">
<div class="section-heading">
<div>
<p class="section-kicker">Направления</p>
<h2>Подготовка в нужном темпе</h2>
</div>
<p>Подберём фокус консультации под текущий уровень, цели и дату экзамена.</p>
<div class="section-label"><span>02</span><p>Направления</p></div>
<h2>Работаем на<br><em>вашу цель</em></h2>
<p>Выбираем темп и программу с учётом класса, стартового уровня и даты экзамена.</p>
</div>
<div class="subject-grid">
<article class="subject-card physics-card">
<span class="subject-icon">F</span>
<p class="card-kicker">Предмет</p>
<div class="program-grid">
<article class="program-card physics-card">
<div class="program-top"><span class="program-code">01 / PH</span><span class="program-symbol">F</span></div>
<h3>Физика</h3>
<p>От формул и единиц измерения до сложных задач второй части.</p>
<ul>
<li>Механика, термодинамика, электродинамика</li>
<li>Задачи с формулами и графиками</li>
<li>Подготовка к ОГЭ и ЕГЭ</li>
<li>ОГЭ и ЕГЭ</li>
<li>Механика, электричество, оптика</li>
<li>Графики, эксперименты, расчёты</li>
</ul>
<a href="#booking">Записаться <span aria-hidden="true"></span></a>
<a href="#booking">Выбрать консультацию <span></span></a>
</article>
<article class="subject-card math-card">
<span class="subject-icon">π</span>
<p class="card-kicker">Предмет</p>
<article class="program-card math-card">
<div class="program-top"><span class="program-code">02 / MTH</span><span class="program-symbol">π</span></div>
<h3>Математика</h3>
<p>Понятная алгебра и геометрия вместо набора непонятных правил.</p>
<ul>
<li>Алгебра, геометрия, вероятность</li>
<li>Уравнения, неравенства, задачи</li>
<li>Подготовка к ОГЭ и ЕГЭ</li>
<li>ОГЭ и ЕГЭ</li>
<li>Уравнения, неравенства, функции</li>
<li>Планиметрия и стереометрия</li>
</ul>
<a href="#booking">Записаться <span aria-hidden="true"></span></a>
<a href="#booking">Выбрать консультацию <span></span></a>
</article>
</div>
</div>
</section>
<section class="section process">
<div class="container">
<p class="section-kicker">Как это работает</p>
<h2>Три простых шага</h2>
<div class="steps">
<article><span>01</span><h3>Оставьте заявку</h3><p>Выберите предмет, экзамен и удобное время.</p></article>
<article><span>02</span><h3>Подтвердим время</h3><p>Игорь Олегович свяжется с вами по указанным контактам.</p></article>
<article><span>03</span><h3>Начнём подготовку</h3><p>Определим цель и разберём первые задачи.</p></article>
<section class="section approach">
<div class="container approach-layout">
<div class="approach-copy">
<div class="section-label"><span>03</span><p>Как проходят занятия</p></div>
<h2>Не «натаскивание»,<br>а <em>система.</em></h2>
</div>
<div class="approach-steps">
<article><span>01</span><h3>Диагностика</h3><p>Определяем уровень и темы, на которые стоит обратить внимание в первую очередь.</p></article>
<article><span>02</span><h3>План</h3><p>Собираем маршрут подготовки: теория, практика, повторение и работа с ошибками.</p></article>
<article><span>03</span><h3>Уверенность</h3><p>Тренируем самостоятельное решение и разбираем вопросы до полного понимания.</p></article>
</div>
</div>
</section>
<section class="booking" id="booking">
<section class="booking-section" id="booking">
<div class="container booking-layout">
<div class="booking-copy">
<p class="section-kicker">Запись на консультацию</p>
<h2>Сделайте первый шаг к уверенному экзамену.</h2>
<p>Заполните форму — время будет окончательно подтверждено после связи с преподавателем.</p>
<p class="privacy-note">Контакты используются только для ответа на вашу заявку.</p>
<div class="booking-intro">
<p class="eyebrow eyebrow-light"><span></span> Онлайн-запись</p>
<h2>Выберите удобное время <em>для старта.</em></h2>
<p>Слот предварительно бронируется за вами. Игорь Олегович подтвердит консультацию по указанным контактам.</p>
<div class="availability-card">
<span class="availability-dot"></span>
<div><strong>{{ available_slot_count }}</strong><p>свободных окон<br>для записи сейчас</p></div>
</div>
<p class="booking-help">Не нашли подходящее время? Напишите желаемый день в комментарии — постараемся подобрать вариант.</p>
</div>
<div class="form-card">
<div class="booking-form-card">
{% if messages %}
{% for message in messages %}
<div class="message message-{{ message.tags }}" role="status">{{ message }}</div>
<div class="form-message form-message-{{ message.tags }}" role="status">{{ message }}</div>
{% endfor %}
{% endif %}
{% if form.non_field_errors %}
<div class="form-errors">{{ form.non_field_errors }}</div>
{% endif %}
<div class="form-title"><span>Запись на консультацию</span><p>Поля со звёздочкой обязательны</p></div>
{% if form.non_field_errors %}<div class="form-errors">{{ form.non_field_errors }}</div>{% endif %}
<form method="post" action="#booking" novalidate>
{% csrf_token %}
<div class="form-grid">
<div class="field full-width">
<label for="{{ form.name.id_for_label }}">{{ form.name.label }}</label>
{{ form.name }}
{% for error in form.name.errors %}<span class="error">{{ error }}</span>{% endfor %}
<div class="field full">
<label for="{{ form.name.id_for_label }}">{{ form.name.label }} <b>*</b></label>
{{ form.name }}{% for error in form.name.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.phone.id_for_label }}">{{ form.phone.label }}</label>
{{ form.phone }}
{% for error in form.phone.errors %}<span class="error">{{ error }}</span>{% endfor %}
<label for="{{ form.phone.id_for_label }}">{{ form.phone.label }} <b>*</b></label>
{{ form.phone }}{% for error in form.phone.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.contact.id_for_label }}">{{ form.contact.label }}</label>
{{ form.contact }}
{% for error in form.contact.errors %}<span class="error">{{ error }}</span>{% endfor %}
{{ form.contact }}{% for error in form.contact.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.subject.id_for_label }}">{{ form.subject.label }}</label>
{{ form.subject }}
{% for error in form.subject.errors %}<span class="error">{{ error }}</span>{% endfor %}
<label for="{{ form.student_grade.id_for_label }}">{{ form.student_grade.label }}</label>
{{ form.student_grade }}{% for error in form.student_grade.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.exam.id_for_label }}">{{ form.exam.label }}</label>
{{ form.exam }}
{% for error in form.exam.errors %}<span class="error">{{ error }}</span>{% endfor %}
<label for="{{ form.subject.id_for_label }}">{{ form.subject.label }} <b>*</b></label>
{{ form.subject }}{% for error in form.subject.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.preferred_date.id_for_label }}">{{ form.preferred_date.label }}</label>
{{ form.preferred_date }}
{% for error in form.preferred_date.errors %}<span class="error">{{ error }}</span>{% endfor %}
<label for="{{ form.exam.id_for_label }}">{{ form.exam.label }} <b>*</b></label>
{{ form.exam }}{% for error in form.exam.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field">
<label for="{{ form.preferred_time.id_for_label }}">{{ form.preferred_time.label }}</label>
{{ form.preferred_time }}
{% for error in form.preferred_time.errors %}<span class="error">{{ error }}</span>{% endfor %}
<label for="{{ form.format.id_for_label }}">{{ form.format.label }} <b>*</b></label>
{{ form.format }}{% for error in form.format.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field full-width">
<div class="field full">
<label for="{{ form.slot.id_for_label }}">{{ form.slot.label }} <b>*</b></label>
{{ form.slot }}{% for error in form.slot.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
<div class="field full">
<label for="{{ form.comment.id_for_label }}">{{ form.comment.label }}</label>
{{ form.comment }}
{% for error in form.comment.errors %}<span class="error">{{ error }}</span>{% endfor %}
{{ form.comment }}{% for error in form.comment.errors %}<span class="error">{{ error }}</span>{% endfor %}
</div>
</div>
<button class="button button-primary submit-button" type="submit">Отправить заявку</button>
<label class="consent-label" for="{{ form.consent_given.id_for_label }}">
{{ form.consent_given }}
<span>Я согласен на обработку персональных данных в соответствии с <a href="{% url 'consultations:privacy_policy' %}">политикой конфиденциальности</a>. <b>*</b></span>
</label>
{% for error in form.consent_given.errors %}<span class="error consent-error">{{ error }}</span>{% endfor %}
{% if available_slot_count %}
<button class="button button-primary form-submit" type="submit">Отправить заявку <span aria-hidden="true"></span></button>
{% else %}
<button class="button button-disabled form-submit" type="button" disabled>Свободных окон пока нет</button>
{% endif %}
</form>
</div>
</div>
</section>
<section class="section faq" id="faq">
<div class="container faq-layout">
<div><div class="section-label"><span>04</span><p>Вопросы</p></div><h2>Перед первым<br><em>занятием</em></h2></div>
<div class="faq-list">
<details open><summary>Как записаться на консультацию?<span>+</span></summary><p>Выберите свободное время, заполните форму и дождитесь подтверждения. Контакты нужны, чтобы согласовать детали занятия.</p></details>
<details><summary>Можно ли начать подготовку не с начала года?<span>+</span></summary><p>Да. На первой консультации определим стартовый уровень, ближайшую цель и соберём реалистичный план подготовки.</p></details>
<details><summary>Что указать в комментарии к заявке?<span>+</span></summary><p>Напишите класс, предмет, цель и темы, которые кажутся сложными. Это поможет подготовиться к первой встрече.</p></details>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer-content">
<a class="brand" href="#top">ИО<span>·</span>учёба</a>
<p>Физика и математика · подготовка к ОГЭ и ЕГЭ</p>
<p>© {% now "Y" %} Игорь Олегович Безрукавов</p>
<div class="container footer-main">
<a class="brand brand-footer" href="#top"><span>ИО</span> physics &amp; math</a>
<p>Физика и математика без лишней тревоги.</p>
<a href="#booking">Записаться <span></span></a>
</div>
<div class="container footer-bottom"><span>© {% now "Y" %} Игорь Олегович Безрукавов</span><a href="{% url 'consultations:privacy_policy' %}">Политика конфиденциальности</a></div>
</footer>
</body>
</html>
@@ -0,0 +1,33 @@
{% load static %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Политика конфиденциальности — Игорь Безрукавов</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&amp;family=Source+Serif+4:opsz,wght@8..60,600;8..60,700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body class="privacy-page">
<header class="site-header"><div class="container navigation"><a class="brand" href="{% url 'consultations:home' %}"><span>ИО</span> physics &amp; math</a><a class="header-action" href="{% url 'consultations:home' %}#booking">Записаться <span></span></a></div></header>
<main class="privacy-content container">
<p class="eyebrow"><span></span> Юридическая информация</p>
<h1>Политика<br><em>конфиденциальности</em></h1>
<p class="privacy-lead">Этот сайт использует персональные данные только для обработки заявок на консультации.</p>
<div class="privacy-text">
<h2>Какие данные собираются</h2>
<p>При заполнении формы могут быть указаны имя, номер телефона, способ связи, класс ученика, предмет, цель подготовки и комментарий к заявке.</p>
<h2>Зачем они нужны</h2>
<p>Данные используются исключительно для связи с заявителем, подтверждения времени консультации и подготовки к занятию.</p>
<h2>Хранение и защита</h2>
<p>Доступ к заявкам имеет только преподаватель. Данные не передаются третьим лицам и не используются для рекламных рассылок.</p>
<h2>Отзыв согласия</h2>
<p>Чтобы уточнить, изменить или удалить сведения из заявки, свяжитесь с преподавателем тем же способом, который был указан при записи.</p>
</div>
<a class="button button-primary" href="{% url 'consultations:home' %}#booking">Вернуться к записи <span></span></a>
</main>
<footer class="site-footer"><div class="container footer-bottom"><span>© {% now "Y" %} Игорь Олегович Безрукавов</span><a href="{% url 'consultations:home' %}">На главную</a></div></footer>
</body>
</html>