Corp/backend/templates/auth/register.html

262 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'base.html' %}
{% load static django_bootstrap5 %}
{% block title %}Регистрация сотрудника{% endblock %}
{# ВАЖНО: применяем тему ещё в <head>, чтобы не было «мигания» темы #}
{% block extra_css %}
<script src="{% static 'js/utils/themeInitializer.js' %}"></script>
{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow">
{# Заголовок без жёсткой покраски — следуем теме #}
<div class="card-header bg-primary text-white">
<h3 class="mb-0">Регистрация нового сотрудника</h3>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
<!-- Контакты -->
<div class="row g-3 mb-4">
<div class="col-md-6">
{% bootstrap_field form.email show_label=False placeholder="E-mail" %}
</div>
<div class="col-md-6">
{% bootstrap_field form.phone_number show_label=False placeholder="Телефон" %}
</div>
</div>
<!-- Мессенджеры -->
<div class="row g-3 mb-4">
<div class="col-md-4 position-relative">
{% bootstrap_field form.whatsapp show_label=False placeholder="WhatsApp" %}
<span class="position-absolute top-50 end-0 translate-middle-y me-3">
<i class="bi bi-whatsapp text-success"></i>
</span>
</div>
<div class="col-md-4 position-relative">
{% bootstrap_field form.telegram show_label=False placeholder="Telegram" %}
<span class="position-absolute top-50 end-0 translate-middle-y me-3">
<i class="bi bi-telegram"></i>
</span>
</div>
<div class="col-md-4 position-relative">
{% bootstrap_field form.wechat show_label=False placeholder="WeChat" %}
<span class="position-absolute top-50 end-0 translate-middle-y me-3">
<i class="bi bi-wechat" style="color:#07C160;"></i>
</span>
</div>
{% if form.non_field_errors %}
<div class="alert alert-danger alert-dismissible fade show mb-0 mt-3" style="border-radius:8px;">
{% for error in form.non_field_errors %}
<i class="bi bi-exclamation-circle me-2"></i>{{ error }}
{% endfor %}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endif %}
</div>
<!-- ФИО -->
<div class="row g-3 mb-4">
<div class="col-md-4">{% bootstrap_field form.last_name show_label=False placeholder="Фамилия" %}</div>
<div class="col-md-4">{% bootstrap_field form.first_name show_label=False placeholder="Имя" %}</div>
<div class="col-md-4">{% bootstrap_field form.patronymic show_label=False placeholder="Отчество" %}</div>
</div>
<!-- Дата рождения + Пол -->
<div class="row g-3 mb-4">
<div class="col-md-4">
{% bootstrap_field form.birth_date show_label=False placeholder="Дата рождения" %}
</div>
<div class="col-md-8">
<label class="form-label d-block mb-2">Пол <span class="text-danger">*</span></label>
{% bootstrap_field form.gender show_label=False %}
</div>
</div>
<!-- Пароли -->
<div class="row g-3 mb-4">
<div class="col-md-6 position-relative">
{% bootstrap_field form.password1 show_label=False placeholder="Пароль" %}
<span class="password-toggle position-absolute top-50 end-0 translate-middle-y me-3 pe-5"
data-target="#{{ form.password1.auto_id }}"
style="cursor:pointer; z-index:10;">
<i class="bi bi-eye-slash"></i>
</span>
</div>
<div class="col-md-6 position-relative">
{% bootstrap_field form.password2 show_label=False placeholder="Повторите пароль" %}
<span class="password-toggle position-absolute top-50 end-0 translate-middle-y me-3 pe-5"
data-target="#{{ form.password2.auto_id }}"
style="cursor:pointer; z-index:10;">
<i class="bi bi-eye-slash"></i>
</span>
</div>
</div>
<!-- Аватар с превью -->
<div class="row g-3 align-items-center mb-4">
<div class="col-auto">
<div class="rounded-circle border bg-body-secondary d-flex align-items-center justify-content-center"
style="width:96px;height:96px;overflow:hidden;">
<img id="avatarPreview" src=""
alt="Превью аватара"
class="d-none"
style="width:100%;height:100%;object-fit:cover;">
<i id="avatarPlaceholder" class="bi bi-person-circle fs-1 text-secondary"></i>
</div>
</div>
<div class="col">
{% bootstrap_field form.avatar %}
<small class="text-muted">Рекомендуемый размер: 200×200 px</small>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary btn-lg">
<i class="bi bi-person-plus me-2"></i>Зарегистрироваться
</button>
<a href="{% url 'auth_front:login' %}" class="btn btn-link">Уже есть аккаунт? Войти</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script type="module">
import { initPasswordToggle } from "{% static 'js/components/passwordToggleHandler.js' %}";
import { initAvatarPreview } from "{% static 'js/components/avatarPreviewHandler.js' %}";
import { initLiveValidation } from "{% static 'js/components/liveValidationHandler.js' %}";
document.addEventListener('DOMContentLoaded', function () {
// Переключение видимости пароля
initPasswordToggle();
// Превью аватара
initAvatarPreview({
fileInputSelector: 'input[type="file"][name="{{ form.avatar.html_name }}"]',
previewImageId: 'avatarPreview',
placeholderId: 'avatarPlaceholder'
});
// Live validation - валидация в реальном времени
const validator = initLiveValidation({
formSelector: 'form',
debounce: 500, // задержка 500мс после ввода
rules: {
email: [
{ type: 'required', message: 'Email обязателен для заполнения' },
{ type: 'email', message: 'Введите корректный email (example@company.com)' }
],
phone_number: [
{ type: 'required', message: 'Номер телефона обязателен' },
{
type: 'pattern',
pattern: /^\+\d{11,14}$/,
message: 'Введите номер в формате +79991234567 (11-14 цифр с плюсом)'
}
],
first_name: [
{ type: 'required', message: 'Имя обязательно для заполнения' },
{ type: 'minLength', value: 2, message: 'Минимум 2 символа' },
{
type: 'pattern',
pattern: /^[^\d]+$/,
message: 'Имя не должно содержать цифры'
}
],
last_name: [
{ type: 'required', message: 'Фамилия обязательна для заполнения' },
{ type: 'minLength', value: 2, message: 'Минимум 2 символа' },
{
type: 'pattern',
pattern: /^[^\d]+$/,
message: 'Фамилия не должна содержать цифры'
}
],
patronymic: [
{
type: 'pattern',
pattern: /^[^\d]*$/,
message: 'Отчество не должно содержать цифры',
required: false // Поле опциональное
}
],
birth_date: [
{ type: 'required', message: 'Дата рождения обязательна' },
{
type: 'custom',
validate: (value) => {
const date = new Date(value);
const today = new Date();
const age = today.getFullYear() - date.getFullYear();
return age >= 18 && age <= 100;
},
message: 'Возраст должен быть от 18 до 100 лет'
}
],
gender: [
{ type: 'required', message: 'Необходимо указать пол' }
],
avatar: [
{ type: 'required', message: 'Необходимо загрузить фотографию' }
],
password1: [
{ type: 'required', message: 'Пароль обязателен' },
{ type: 'minLength', value: 6, message: 'Пароль должен содержать минимум 6 символов' }
],
password2: [
{ type: 'required', message: 'Подтвердите пароль' },
{
type: 'match',
field: 'password1',
message: 'Пароли не совпадают'
}
],
// Специальная проверка контактов (хотя бы один)
telegram: [
{
type: 'anyContact',
fields: ['telegram', 'whatsapp', 'wechat'],
message: 'Заполните хотя бы один мессенджер (Telegram, WhatsApp или WeChat)'
}
],
whatsapp: [
{
type: 'anyContact',
fields: ['telegram', 'whatsapp', 'wechat'],
message: 'Заполните хотя бы один мессенджер (Telegram, WhatsApp или WeChat)'
}
],
wechat: [
{
type: 'anyContact',
fields: ['telegram', 'whatsapp', 'wechat'],
message: 'Заполните хотя бы один мессенджер (Telegram, WhatsApp или WeChat)'
}
]
}
});
// При изменении password1 - ревалидируем password2
document.querySelector('[name="password1"]')?.addEventListener('input', () => {
const password2 = document.querySelector('[name="password2"]');
if (password2 && password2.value) {
validator.validateField('password2');
}
});
});
</script>
{% endblock %}