378 lines
16 KiB
HTML
378 lines
16 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Добавление клиента{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
<div class="row justify-content-center">
|
||
<div class="col-md-8">
|
||
<div class="card">
|
||
<div class="card-header" style="background: linear-gradient(135deg, var(--steel-blue) 0%, var(--light-steel-blue) 100%); color: white;">
|
||
<h2 class="card-title mb-0">
|
||
<i class="fas fa-user-plus"></i> Добавление нового клиента
|
||
</h2>
|
||
</div>
|
||
<div class="card-body">
|
||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||
{% if messages %}
|
||
{% for category, message in messages %}
|
||
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show">
|
||
{{ message }}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
</div>
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% endwith %}
|
||
|
||
<form method="POST" id="clientForm">
|
||
<div class="row">
|
||
<!-- Основная информация -->
|
||
<div class="col-md-6">
|
||
<h5 class="border-bottom pb-2 mb-3" style="color: var(--steel-blue);">Основная информация</h5>
|
||
|
||
<div class="mb-3">
|
||
<label for="name" class="form-label">Название компании (ООО "Ромашка")</label>
|
||
<input type="text" class="form-control" id="name" name="name" required maxlength="255"
|
||
onkeypress="return blockNumbers(event)" oninput="this.value = this.value.replace(/[0-9]/g, '')"
|
||
placeholder="Например: ООО МебельСтиль">
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="director" class="form-label">Директор (ФИО)</label>
|
||
<input type="text" class="form-control" id="director" name="director" maxlength="255"
|
||
onkeypress="return blockNumbers(event)" oninput="this.value = this.value.replace(/[0-9]/g, '')"
|
||
placeholder="Фамилия Имя Отчество">
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="contractor_type_id" class="form-label">Тип клиента</label>
|
||
<select class="form-select" id="contractor_type_id" name="contractor_type_id" required>
|
||
<option value="">Выберите тип клиента</option>
|
||
{% for type in contractor_types %}
|
||
<option value="{{ type.id }}">{{ type.type_name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="rating" class="form-label">Рейтинг (от 0.0 до 5.0)</label>
|
||
<input type="number" class="form-control" id="rating" name="rating"
|
||
min="0" max="5" step="0.1" value="5.0"
|
||
placeholder="Например: 4.5">
|
||
<div class="form-text">От 0.0 до 5.0</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Контактная информация -->
|
||
<div class="col-md-6">
|
||
<h5 class="border-bottom pb-2 mb-3" style="color: var(--steel-blue);">Контактная информация</h5>
|
||
|
||
<div class="mb-3">
|
||
<label for="email" class="form-label">Email (company@gmail.com)</label>
|
||
<input type="email" class="form-control" id="email" name="email" required maxlength="255"
|
||
onkeypress="return allowEnglishOnly(event)" oninput="this.value = this.value.replace(/[а-яА-ЯёЁ]/g, '')"
|
||
placeholder="Например: info@company.ru">
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="phone" class="form-label">Телефон (+7 999 999-99-99)</label>
|
||
<input type="tel" class="form-control" id="phone" name="phone" maxlength="50"
|
||
onkeypress="return allowPhoneNumbers(event)"
|
||
placeholder="+7 (999) 999-99-99">
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="inn" class="form-label">ИНН (10 или 12 цифр)</label>
|
||
<input type="text" class="form-control" id="inn" name="inn" maxlength="12"
|
||
onkeypress="return allowOnlyNumbers(event)"
|
||
oninput="this.value = this.value.replace(/[^0-9]/g, '').slice(0, 12)"
|
||
placeholder="Например: 7701123456">
|
||
<div class="form-text">Только цифры, 10 или 12 цифр</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="address" class="form-label">Юридический адрес (г. Москва, ул. Ленина, д. 1)</label>
|
||
<textarea class="form-control" id="address" name="address" rows="3" maxlength="500"
|
||
placeholder="Например: 127000, г. Москва, ул. Ленина, д. 5, офис 10"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-3">
|
||
<div class="col-12">
|
||
<div class="d-flex justify-content-between">
|
||
<a href="{{ url_for('index') }}" class="btn btn-secondary">
|
||
<i class="fas fa-arrow-left"></i> Назад к списку
|
||
</a>
|
||
<button type="submit" class="btn btn-success">
|
||
<i class="fas fa-save"></i> Добавить клиента
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.card {
|
||
border: 1px solid var(--light-steel-blue);
|
||
border-radius: 15px;
|
||
box-shadow: 0 4px 10px rgba(70, 130, 180, 0.1);
|
||
}
|
||
|
||
.form-label {
|
||
color: var(--steel-blue);
|
||
font-weight: 500;
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
}
|
||
|
||
.form-control, .form-select {
|
||
border: 2px solid var(--light-steel-blue);
|
||
border-radius: 8px;
|
||
padding: 10px 15px;
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.form-control:focus, .form-select:focus {
|
||
border-color: var(--steel-blue);
|
||
box-shadow: 0 0 0 0.2rem rgba(70, 130, 180, 0.25);
|
||
outline: none;
|
||
}
|
||
|
||
.form-control:hover, .form-select:hover {
|
||
border-color: var(--steel-blue);
|
||
}
|
||
|
||
.btn-success {
|
||
background: linear-gradient(135deg, #28a745 0%, #218838 100%);
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 10px 20px;
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background: linear-gradient(135deg, #218838 0%, #28a745 100%);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 10px rgba(40, 167, 69, 0.3);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 10px 20px;
|
||
color: #2c3e50;
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: linear-gradient(135deg, #9fb6d4 0%, var(--light-steel-blue) 100%);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 10px rgba(176, 196, 222, 0.3);
|
||
}
|
||
|
||
.alert {
|
||
border-radius: 10px;
|
||
border: 1px solid var(--light-steel-blue);
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
}
|
||
|
||
.alert-success {
|
||
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
|
||
color: #155724;
|
||
border-left: 6px solid #28a745;
|
||
}
|
||
|
||
.alert-danger {
|
||
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
|
||
color: #721c24;
|
||
border-left: 6px solid #dc3545;
|
||
}
|
||
|
||
.border-bottom {
|
||
border-color: var(--light-steel-blue) !important;
|
||
}
|
||
|
||
textarea.form-control {
|
||
resize: vertical;
|
||
min-height: 80px;
|
||
}
|
||
|
||
.form-text {
|
||
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
|
||
color: #6c757d;
|
||
}
|
||
|
||
input::placeholder, textarea::placeholder {
|
||
color: #a0aec0;
|
||
font-style: italic;
|
||
font-size: 0.9rem;
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// Запрет на ввод цифр в текстовые поля (название компании, ФИО)
|
||
function blockNumbers(event) {
|
||
const key = event.key;
|
||
// Разрешаем: буквы, пробел, backspace, delete, tab, enter, стрелки
|
||
if (key === ' ' || key === 'Backspace' || key === 'Delete' || key === 'Tab' ||
|
||
key === 'Enter' || key === 'ArrowLeft' || key === 'ArrowRight' ||
|
||
key === 'ArrowUp' || key === 'ArrowDown' || key === 'Home' || key === 'End') {
|
||
return true;
|
||
}
|
||
|
||
// Запрещаем цифры
|
||
if (key >= '0' && key <= '9') {
|
||
event.preventDefault();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// Только латинские буквы для email
|
||
function allowEnglishOnly(event) {
|
||
const key = event.key;
|
||
// Разрешаем: латинские буквы, цифры, специальные символы для email
|
||
if (key === 'Backspace' || key === 'Delete' || key === 'Tab' ||
|
||
key === 'Enter' || key === 'ArrowLeft' || key === 'ArrowRight' ||
|
||
key === 'ArrowUp' || key === 'ArrowDown' || key === 'Home' || key === 'End' ||
|
||
key === '@' || key === '.' || key === '_' || key === '-') {
|
||
return true;
|
||
}
|
||
|
||
// Проверяем, является ли символ латинской буквой или цифрой
|
||
const charCode = event.keyCode || event.which;
|
||
const isLatin = (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122);
|
||
const isNumber = (charCode >= 48 && charCode <= 57);
|
||
|
||
if (isLatin || isNumber) {
|
||
return true;
|
||
}
|
||
|
||
event.preventDefault();
|
||
return false;
|
||
}
|
||
|
||
// Разрешаем только цифры (для ИНН)
|
||
function allowOnlyNumbers(event) {
|
||
const key = event.key;
|
||
// Разрешаем: цифры, backspace, delete, tab, enter, стрелки
|
||
if (key === 'Backspace' || key === 'Delete' || key === 'Tab' ||
|
||
key === 'Enter' || key === 'ArrowLeft' || key === 'ArrowRight' ||
|
||
key === 'ArrowUp' || key === 'ArrowDown' || key === 'Home' || key === 'End') {
|
||
return true;
|
||
}
|
||
|
||
// Разрешаем только цифры
|
||
if (key >= '0' && key <= '9') {
|
||
return true;
|
||
}
|
||
|
||
event.preventDefault();
|
||
return false;
|
||
}
|
||
|
||
// Для поля телефона разрешаем цифры и некоторые символы
|
||
function allowPhoneNumbers(event) {
|
||
const key = event.key;
|
||
// Разрешаем: цифры, +, -, (, ), пробел, backspace и т.д.
|
||
const allowedKeys = ['+', '-', '(', ')', ' '];
|
||
|
||
if (key === 'Backspace' || key === 'Delete' || key === 'Tab' ||
|
||
key === 'Enter' || key === 'ArrowLeft' || key === 'ArrowRight' ||
|
||
key === 'ArrowUp' || key === 'ArrowDown' || key === 'Home' || key === 'End') {
|
||
return true;
|
||
}
|
||
|
||
if (allowedKeys.includes(key)) {
|
||
return true;
|
||
}
|
||
|
||
if (key >= '0' && key <= '9') {
|
||
return true;
|
||
}
|
||
|
||
event.preventDefault();
|
||
return false;
|
||
}
|
||
|
||
// Валидация формы
|
||
document.getElementById('clientForm').addEventListener('submit', function(e) {
|
||
const inn = document.getElementById('inn').value;
|
||
const name = document.getElementById('name').value;
|
||
const director = document.getElementById('director').value;
|
||
const email = document.getElementById('email').value;
|
||
|
||
// Проверка на наличие цифр в названии компании
|
||
if (name.match(/[0-9]/)) {
|
||
e.preventDefault();
|
||
alert('Название компании не должно содержать цифры');
|
||
return false;
|
||
}
|
||
|
||
// Проверка на наличие цифр в ФИО директора
|
||
if (director && director.match(/[0-9]/)) {
|
||
e.preventDefault();
|
||
alert('ФИО директора не должно содержать цифры');
|
||
return false;
|
||
}
|
||
|
||
// Проверка ИНН
|
||
if (inn && !/^\d+$/.test(inn)) {
|
||
e.preventDefault();
|
||
alert('ИНН должен содержать только цифры');
|
||
return false;
|
||
}
|
||
|
||
if (inn && (inn.length !== 10 && inn.length !== 12)) {
|
||
e.preventDefault();
|
||
alert('ИНН должен содержать 10 или 12 цифр');
|
||
return false;
|
||
}
|
||
|
||
// Проверка email на наличие русских букв
|
||
if (email.match(/[а-яА-ЯёЁ]/)) {
|
||
e.preventDefault();
|
||
alert('Email не должен содержать русские буквы');
|
||
return false;
|
||
}
|
||
|
||
// Проверка домена email
|
||
const allowedDomains = ['gmail.com', 'yandex.ru', 'mail.ru', 'bk.ru', 'list.ru', 'inbox.ru', 'rambler.ru', 'ya.ru'];
|
||
const domain = email.split('@')[1];
|
||
|
||
if (domain) {
|
||
const isDomainAllowed = allowedDomains.some(allowed => domain === allowed || domain.endsWith('.' + allowed));
|
||
if (!isDomainAllowed && !domain.endsWith('.ru') && !domain.endsWith('.com')) {
|
||
if (!confirm('Домен ' + domain + ' не входит в список популярных. Продолжить?')) {
|
||
e.preventDefault();
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
const rating = parseFloat(document.getElementById('rating').value);
|
||
if (rating < 0 || rating > 5) {
|
||
e.preventDefault();
|
||
alert('Рейтинг должен быть от 0.0 до 5.0');
|
||
return false;
|
||
}
|
||
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
if (!emailRegex.test(email)) {
|
||
e.preventDefault();
|
||
alert('Введите корректный email адрес');
|
||
return false;
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %} |