Initial commit: Django inventory system
This commit is contained in:
40
myproject/templates/base.html
Normal file
40
myproject/templates/base.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Мой Django Проект{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
padding-top: 56px; /* Add space for fixed navbar */
|
||||
}
|
||||
.form-container {
|
||||
max-width: 400px;
|
||||
margin: 50px auto;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Include the navbar component -->
|
||||
{% include 'navbar.html' %}
|
||||
|
||||
<!-- Сообщения для залогиненных пользователей отображаются здесь -->
|
||||
{% if user.is_authenticated %}
|
||||
{% include 'components/messages.html' %}
|
||||
{% endif %}
|
||||
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
56
myproject/templates/change_password.html
Normal file
56
myproject/templates/change_password.html
Normal file
@@ -0,0 +1,56 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Смена пароля{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<h2 class="text-center mb-4">Смена пароля</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
{% include 'accounts/password_input.html' with field_name=form.old_password.id_for_label field_label='Старый пароль' required=True field_errors=form.old_password.errors %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{% include 'accounts/password_input.html' with field_name=form.new_password1.id_for_label field_label='Новый пароль' required=True field_errors=form.new_password1.errors %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{% include 'accounts/password_input.html' with field_name=form.new_password2.id_for_label field_label='Подтверждение нового пароля' required=True field_errors=form.new_password2.errors %}
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<a href="{% url 'accounts:profile' %}" class="btn btn-outline-secondary me-md-2">
|
||||
<i class="bi bi-arrow-left me-1"></i> Отмена
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save me-1"></i> Сохранить изменения
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Добавляем обработчик для показа/скрытия пароля
|
||||
document.querySelectorAll('.show-password-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetId = this.getAttribute('data-target');
|
||||
const targetInput = document.getElementById(targetId);
|
||||
const icon = this.querySelector('i');
|
||||
|
||||
if (targetInput.type === 'password') {
|
||||
targetInput.type = 'text';
|
||||
icon.classList.remove('bi-eye');
|
||||
icon.classList.add('bi-eye-slash');
|
||||
} else {
|
||||
targetInput.type = 'password';
|
||||
icon.classList.remove('bi-eye-slash');
|
||||
icon.classList.add('bi-eye');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
142
myproject/templates/components/filter_panel.html
Normal file
142
myproject/templates/components/filter_panel.html
Normal file
@@ -0,0 +1,142 @@
|
||||
{% comment %}
|
||||
Переиспользуемый компонент панели фильтрации
|
||||
|
||||
Параметры:
|
||||
- title: заголовок панели (default: "Фильтры")
|
||||
- filters: объект с данными фильтров
|
||||
- categories: список категорий
|
||||
- tags: список тегов
|
||||
- current: текущие значения фильтров
|
||||
- action_buttons: список кнопок действий
|
||||
- url: ссылка
|
||||
- text: текст кнопки
|
||||
- class: CSS классы (default: 'btn-primary')
|
||||
- icon: иконка Bootstrap Icons (без префикса bi-)
|
||||
- show_search: показать поиск (default: True)
|
||||
- show_category: показать фильтр категорий (default: True)
|
||||
- show_status: показать фильтр статуса (default: True)
|
||||
- show_tags: показать фильтр тегов (default: True)
|
||||
|
||||
Пример использования:
|
||||
{% include 'components/filter_panel.html' with title="Товары" filters=filters action_buttons=action_buttons %}
|
||||
{% endcomment %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/filter_panel.css' %}">
|
||||
|
||||
<div class="filter-panel card shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<!-- Заголовок и кнопки действий -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap">
|
||||
<h5 class="card-title mb-0 me-3">
|
||||
<i class="bi bi-funnel-fill"></i> {{ title|default:"Фильтры" }}
|
||||
</h5>
|
||||
|
||||
<!-- Кнопки действий -->
|
||||
{% if action_buttons %}
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
{% for button in action_buttons %}
|
||||
<a href="{{ button.url }}" class="btn {{ button.class|default:'btn-primary' }} btn-sm me-2 mb-2 mb-md-0">
|
||||
{% if button.icon %}<i class="bi bi-{{ button.icon }}"></i>{% endif %}
|
||||
{{ button.text }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<hr class="my-3">
|
||||
|
||||
<!-- Форма фильтров -->
|
||||
<form method="get" id="filterForm">
|
||||
<div class="row g-3">
|
||||
<!-- Поле поиска -->
|
||||
{% if show_search|default:True %}
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="search" class="form-label">
|
||||
<i class="bi bi-search"></i> Поиск
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="search"
|
||||
name="search"
|
||||
placeholder="Поиск по названию..."
|
||||
value="{{ filters.current.search|default:'' }}"
|
||||
>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Фильтр по категории -->
|
||||
{% if show_category|default:True and filters.categories %}
|
||||
<div class="col-12 col-md-3">
|
||||
<label for="category" class="form-label">
|
||||
<i class="bi bi-folder"></i> Категория
|
||||
</label>
|
||||
<select class="form-select" id="category" name="category">
|
||||
<option value="">Все категории</option>
|
||||
{% for cat in filters.categories %}
|
||||
<option value="{{ cat.id }}" {% if filters.current.category|stringformat:"s" == cat.id|stringformat:"s" %}selected{% endif %}>
|
||||
{{ cat.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Фильтр по статусу -->
|
||||
{% if show_status|default:True %}
|
||||
<div class="col-12 col-md-2">
|
||||
<label for="is_active" class="form-label">
|
||||
<i class="bi bi-toggle-on"></i> Статус
|
||||
</label>
|
||||
<select class="form-select" id="is_active" name="is_active">
|
||||
<option value="">Все</option>
|
||||
<option value="1" {% if filters.current.is_active == '1' %}selected{% endif %}>Активные</option>
|
||||
<option value="0" {% if filters.current.is_active == '0' %}selected{% endif %}>Неактивные</option>
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Фильтр по тегам -->
|
||||
{% if show_tags|default:True and filters.tags %}
|
||||
<div class="col-12">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-tags"></i> Теги
|
||||
</label>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for tag in filters.tags %}
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
name="tags"
|
||||
id="tag_{{ tag.id }}"
|
||||
value="{{ tag.id }}"
|
||||
{% if tag.id|stringformat:"s" in filters.current.tags %}checked{% endif %}
|
||||
>
|
||||
<label class="form-check-label" for="tag_{{ tag.id }}">
|
||||
{{ tag.name }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Кнопки управления фильтрами -->
|
||||
<div class="col-12">
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-check-circle"></i> Применить фильтры
|
||||
</button>
|
||||
<a href="{{ request.path }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-x-circle"></i> Сбросить
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
12
myproject/templates/components/messages.html
Normal file
12
myproject/templates/components/messages.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Компонент для отображения Django Messages -->
|
||||
<!-- Использование: include 'components/messages.html' -->
|
||||
{% if messages %}
|
||||
<div class="container mt-3">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
12
myproject/templates/dashboard.html
Normal file
12
myproject/templates/dashboard.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Панель управления{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Добро пожаловать, {{ user.name|default:user.email }}!</h1>
|
||||
<p class="lead">Вы успешно вошли в систему.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
22
myproject/templates/home.html
Normal file
22
myproject/templates/home.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Главная страница{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Добро пожаловать!</h1>
|
||||
<p class="lead">Система аутентификации с подтверждением email</p>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<p>Рады видеть вас снова, {{ user.name|default:user.email }}!</p>
|
||||
<a href="{% url 'index' %}" class="btn btn-primary">Перейти в панель управления</a>
|
||||
{% else %}
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'accounts:login' %}" class="btn btn-primary me-2">Войти</a>
|
||||
<a href="{% url 'accounts:register' %}" class="btn btn-secondary">Регистрация</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
138
myproject/templates/index.html
Normal file
138
myproject/templates/index.html
Normal file
@@ -0,0 +1,138 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Регистрация / Вход{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Контейнер для сообщений об ошибках - фиксированное место -->
|
||||
<!-- ВАЖНО: На главной странице (регистрация/вход) показываем только ошибки -->
|
||||
<div id="messages-container" style="min-height: 60px;">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
{% if 'danger' in message.tags or 'error' in message.tags %}
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-center mb-4">Добро пожаловать</h2>
|
||||
|
||||
<!-- Вкладки для переключения между регистрацией и входом -->
|
||||
<ul class="nav nav-tabs mb-4">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if not request.GET.tab or request.GET.tab == 'register' %}active{% endif %}" data-bs-toggle="tab" href="#register">Регистрация</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.GET.tab == 'login' %}active{% endif %}" data-bs-toggle="tab" href="#login">Вход</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Содержимое вкладок -->
|
||||
<div class="tab-content">
|
||||
<!-- Вкладка регистрации -->
|
||||
<div class="tab-pane fade {% if not request.GET.tab or request.GET.tab == 'register' %}show active{% endif %}" id="register">
|
||||
<form method="post" action="{% url 'accounts:register' %}">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.name.id_for_label }}" class="form-label">Имя</label>
|
||||
{{ form.name }}
|
||||
{% if form.name.errors %}
|
||||
<div class="text-danger">{{ form.name.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label">Email</label>
|
||||
{{ form.email }}
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger">{{ form.email.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'accounts/password_input.html' with field_name=form.password1.id_for_label field_label='Пароль' required=True field_errors=form.password1.errors %}
|
||||
{% include 'accounts/password_input.html' with field_name=form.password2.id_for_label field_label='Подтверждение пароля' required=True field_errors=form.password2.errors %}
|
||||
<button type="submit" class="btn btn-primary w-100">Зарегистрироваться</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Вкладка входа -->
|
||||
<div class="tab-pane fade {% if request.GET.tab == 'login' %}show active{% endif %}" id="login">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
{% include 'accounts/password_input.html' with field_name='password' field_label='Пароль' required=True %}
|
||||
<button type="submit" class="btn btn-primary w-100">Войти</button>
|
||||
|
||||
<!-- Ссылка "Забыли пароль?" -->
|
||||
<div class="text-center mt-3">
|
||||
<a href="#" class="text-decoration-none" data-bs-toggle="modal" data-bs-target="#passwordResetModal">Забыли пароль?</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно для восстановления пароля -->
|
||||
<div class="modal fade" id="passwordResetModal" tabindex="-1" aria-labelledby="passwordResetModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="passwordResetModalLabel">Восстановление пароля</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="passwordResetForm" method="post" action="{% url 'accounts:password_reset' %}">
|
||||
{% csrf_token %}
|
||||
<p>Пожалуйста, введите ваш email, и мы отправим вам инструкции по восстановлению пароля.</p>
|
||||
<div class="mb-3">
|
||||
<label for="resetEmail" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="resetEmail" name="email" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-primary" form="passwordResetForm">Отправить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Управление вкладками
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const tab = urlParams.get('tab');
|
||||
|
||||
if (tab === 'login') {
|
||||
// Переключаемся на вкладку входа
|
||||
const loginTab = document.querySelector('a[href="#login"]');
|
||||
if(loginTab) {
|
||||
bootstrap.Tab.getOrCreateInstance(loginTab).show();
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем обработчик для показа/скрытия пароля
|
||||
document.querySelectorAll('.show-password-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetId = this.getAttribute('data-target');
|
||||
const targetInput = document.getElementById(targetId);
|
||||
const icon = this.querySelector('i');
|
||||
|
||||
if (targetInput.type === 'password') {
|
||||
targetInput.type = 'text';
|
||||
icon.classList.remove('bi-eye');
|
||||
icon.classList.add('bi-eye-slash');
|
||||
} else {
|
||||
targetInput.type = 'password';
|
||||
icon.classList.remove('bi-eye-slash');
|
||||
icon.classList.add('bi-eye');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
50
myproject/templates/login.html
Normal file
50
myproject/templates/login.html
Normal file
@@ -0,0 +1,50 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Вход{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="login">
|
||||
<!-- Контейнер для сообщений об ошибках - зарезервированное место -->
|
||||
<!-- ВАЖНО: Показываем только ошибки входа (danger/error), игнорируем success/info и т.д. -->
|
||||
<div id="messages-container" style="min-height: 60px;">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
{% if 'danger' in message.tags or 'error' in message.tags %}
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-center mb-4">Вход</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
{% include 'accounts/password_input.html' with field_name='password' field_label='Пароль' required=True %}
|
||||
<button type="submit" class="btn btn-primary w-100">Войти</button>
|
||||
</form>
|
||||
|
||||
<!-- Ссылка на регистрацию -->
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'accounts:register' %}" class="text-decoration-none">Нет аккаунта? Зарегистрируйтесь</a>
|
||||
</div>
|
||||
|
||||
<!-- Ссылка "Забыли пароль?" -->
|
||||
<div class="text-center mt-2">
|
||||
<a href="{% url 'accounts:password_reset' %}" class="text-decoration-none">Забыли пароль?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
61
myproject/templates/navbar.html
Normal file
61
myproject/templates/navbar.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!-- navbar.html - Reusable navigation bar component -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
||||
<div class="container">
|
||||
<!-- Brand/Logo -->
|
||||
<a class="navbar-brand" href="{% url 'products:product-list' %}">Склад</a>
|
||||
|
||||
<!-- Toggler for mobile view -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<!-- Navbar content -->
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
{% if user.is_authenticated %}
|
||||
<!-- Dropdown menu for Products -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="productsDropdown" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Товары
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="productsDropdown">
|
||||
<li><a class="dropdown-item" href="{% url 'products:product-list' %}">Все товары</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'products:category-list' %}">Категории</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'products:productkit-list' %}">Комплекты</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{% url 'products:product-create' %}">Создать товар</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'products:category-create' %}">Создать категорию</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav align-items-center">
|
||||
{% if user.is_authenticated %}
|
||||
<!-- Show profile button and logout button for authenticated users -->
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-outline-primary me-2" href="{% url 'accounts:profile' %}">Профиль</a>
|
||||
</li>
|
||||
<li class="nav-item d-flex align-items-center mx-2">
|
||||
<span class="navbar-text mb-0">
|
||||
({{ user.name|default:user.email }})
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-outline-secondary ms-2" href="{% url 'accounts:logout' %}">Выйти</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<!-- Show login and register buttons for non-authenticated users -->
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-outline-primary me-2" href="{% url 'accounts:login' %}">Вход</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-outline-secondary" href="{% url 'accounts:register' %}">Регистрация</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
103
myproject/templates/profile.html
Normal file
103
myproject/templates/profile.html
Normal file
@@ -0,0 +1,103 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Профиль{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid px-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-lg-10 col-xl-8">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white text-center py-3 py-md-4">
|
||||
<h3 class="mb-0">
|
||||
<i class="bi bi-person-circle me-2 d-block d-md-inline"></i>
|
||||
Ваш профиль
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body p-3 p-md-4">
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-md-4 text-center">
|
||||
<div class="profile-icon bg-light rounded-circle d-flex align-items-center justify-content-center mx-auto mb-3"
|
||||
style="width: 100px; height: 100px;">
|
||||
<i class="bi bi-person-fill" style="font-size: 3rem; color: #0d6efd;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-8">
|
||||
<div class="profile-info">
|
||||
<div class="d-flex flex-column flex-md-row mb-3">
|
||||
<div class="me-md-4 mb-2 mb-md-0">
|
||||
<i class="bi bi-person-fill text-primary me-2"></i>
|
||||
<span class="fw-bold">Имя:</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted">{{ user.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row mb-3">
|
||||
<div class="me-md-4 mb-2 mb-md-0">
|
||||
<i class="bi bi-envelope-fill text-primary me-2"></i>
|
||||
<span class="fw-bold">Email:</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted">{{ user.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row mb-3">
|
||||
<div class="me-md-4 mb-2 mb-md-0">
|
||||
<i class="bi bi-shield-check text-primary me-2"></i>
|
||||
<span class="fw-bold">Статус email:</span>
|
||||
</div>
|
||||
<div>
|
||||
{% if user.is_email_confirmed %}
|
||||
<span class="badge bg-success d-inline-flex align-items-center w-auto">
|
||||
<i class="bi bi-check-circle me-1"></i> Подтвержден
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark d-inline-flex align-items-center w-auto">
|
||||
<i class="bi bi-exclamation-circle me-1"></i> Не подтвержден
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row justify-content-end gap-2 mt-4 pt-3 border-top">
|
||||
<a href="{% url 'index' %}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-1"></i> Назад
|
||||
</a>
|
||||
|
||||
{% if not user.is_email_confirmed %}
|
||||
<button class="btn btn-outline-warning" type="button" onclick="resendConfirmation()">
|
||||
<i class="bi bi-envelope me-1"></i> Повторить письмо
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url 'accounts:change_password' %}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-key me-1"></i> Сменить пароль
|
||||
</a>
|
||||
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="editProfile()">
|
||||
<i class="bi bi-pencil me-1"></i> Редактировать
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function resendConfirmation() {
|
||||
// In a real application, this would make an AJAX request to resend the confirmation email
|
||||
alert('Письмо для подтверждения отправлено на ваш email!');
|
||||
}
|
||||
|
||||
function editProfile() {
|
||||
// For now, we'll just show an alert; in a real app this would open an edit form
|
||||
alert('Функция редактирования профиля будет реализована позже.');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
41
myproject/templates/register.html
Normal file
41
myproject/templates/register.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Регистрация{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="form-container">
|
||||
<h2 class="text-center mb-4">Регистрация</h2>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="register">
|
||||
<form method="post" action="{% url 'accounts:register' %}">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.name.id_for_label }}" class="form-label">Имя</label>
|
||||
{{ form.name }}
|
||||
{% if form.name.errors %}
|
||||
<div class="text-danger">{{ form.name.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label">Email</label>
|
||||
{{ form.email }}
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger">{{ form.email.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'accounts/password_input.html' with field_name=form.password1.id_for_label field_label='Пароль' required=True field_errors=form.password1.errors %}
|
||||
{% include 'accounts/password_input.html' with field_name=form.password2.id_for_label field_label='Подтверждение пароля' required=True field_errors=form.password2.errors %}
|
||||
<button type="submit" class="btn btn-primary w-100">Зарегистрироваться</button>
|
||||
</form>
|
||||
|
||||
<!-- Ссылка на вход -->
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'accounts:login' %}" class="text-decoration-none">Уже есть аккаунт? Войти</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user