my-project/templates/edit_client.html
2026-03-23 11:35:48 +05:00

396 lines
17 KiB
HTML

{% extends "base.html" %}
{% block title %}Редактирование клиента{% endblock %}
{% block content %}
<div class="container mt-4">
<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-edit"></i> Редактирование клиента
</h2>
</div>
<div class="card-body">
<!-- Flash сообщения -->
{% 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"
value="{{ client.name }}" required maxlength="255"
onkeypress="return blockNumbers(event)" onpaste="return blockNumbersPaste(event)">
</div>
<div class="mb-3">
<label for="director" class="form-label">Директор (ФИО)</label>
<input type="text" class="form-control" id="director" name="director"
value="{{ client.director or '' }}" maxlength="255"
onkeypress="return blockNumbers(event)" onpaste="return blockNumbersPaste(event)">
</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 }}"
{% if type.id == client.contractor_type_id %}selected{% endif %}>
{{ type.type_name }}
</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="rating" class="form-label">Рейтинг</label>
<input type="number" class="form-control" id="rating" name="rating"
min="0" max="5" step="0.1" value="{{ client.rating or 0.0 }}">
<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</label>
<input type="email" class="form-control" id="email" name="email"
value="{{ client.email }}" required maxlength="255">
</div>
<div class="mb-3">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" class="form-control" id="phone" name="phone"
value="{{ client.phone or '' }}" maxlength="50"
onkeypress="return allowPhoneNumbers(event)">
</div>
<div class="mb-3">
<label for="inn" class="form-label">ИНН</label>
<input type="text" class="form-control" id="inn" name="inn"
value="{{ client.inn or '' }}" maxlength="20" pattern="\d{10,12}"
onkeypress="return allowOnlyNumbers(event)" onpaste="return allowNumbersPaste(event)">
<div class="form-text">10 или 12 цифр</div>
</div>
<div class="mb-3">
<label for="address" class="form-label">Юридический адрес</label>
<textarea class="form-control" id="address" name="address"
rows="3" maxlength="500">{{ client.address or '' }}</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>
<div>
<a href="{{ url_for('order_history', client_id=client.id) }}"
class="btn btn-info me-2">
<i class="fas fa-history"></i> История заказов
</a>
<button type="submit" class="btn btn-success">
<i class="fas fa-save"></i> Сохранить изменения
</button>
</div>
</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);
}
.btn-info {
background: linear-gradient(135deg, var(--steel-blue) 0%, #3a6d99 100%);
border: none;
border-radius: 8px;
padding: 10px 20px;
color: white;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
transition: all 0.3s ease;
}
.btn-info:hover {
background: linear-gradient(135deg, #3a6d99 0%, var(--steel-blue) 100%);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(70, 130, 180, 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;
}
</style>
{% block scripts %}
<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();
showNotification('Цифры запрещены в этом поле');
return false;
}
// Разрешаем буквы (любые)
return true;
}
// Запрет на вставку цифр через Ctrl+V
function blockNumbersPaste(event) {
event.preventDefault();
const pastedText = (event.clipboardData || window.clipboardData).getData('text');
// Удаляем все цифры из вставляемого текста
const cleanedText = pastedText.replace(/[0-9]/g, '');
// Вставляем очищенный текст
const input = event.target;
const start = input.selectionStart;
const end = input.selectionEnd;
const currentValue = input.value;
const newValue = currentValue.substring(0, start) + cleanedText + currentValue.substring(end);
input.value = newValue;
// Возвращаем курсор в конец вставленного текста
input.selectionStart = input.selectionEnd = start + cleanedText.length;
if (pastedText.match(/[0-9]/)) {
showNotification('Цифры были удалены из вставленного текста');
}
}
// Разрешаем только цифры (для ИНН)
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();
showNotification('Только цифры разрешены в этом поле');
return false;
}
// Разрешаем вставку только цифр (для ИНН)
function allowNumbersPaste(event) {
event.preventDefault();
const pastedText = (event.clipboardData || window.clipboardData).getData('text');
// Оставляем только цифры
const cleanedText = pastedText.replace(/[^0-9]/g, '');
// Вставляем очищенный текст
const input = event.target;
const start = input.selectionStart;
const end = input.selectionEnd;
const currentValue = input.value;
const newValue = currentValue.substring(0, start) + cleanedText + currentValue.substring(end);
input.value = newValue;
// Возвращаем курсор в конец вставленного текста
input.selectionStart = input.selectionEnd = start + cleanedText.length;
if (pastedText.match(/[^0-9]/)) {
showNotification('Были вставлены только цифры, остальные символы удалены');
}
}
// Для поля телефона разрешаем цифры и некоторые символы
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;
}
// Показать уведомление
function showNotification(message) {
// Можно использовать alert или создать более красивое уведомление
console.log(message);
// Для простоты используем alert, но можно заменить на что-то более элегантное
// alert(message);
}
// Валидация формы
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;
// Проверка на наличие цифр в названии компании (дополнительная защита)
if (name.match(/[0-9]/)) {
e.preventDefault();
alert('Название компании не должно содержать цифры');
return false;
}
// Проверка на наличие цифр в ФИО директора
if (director && director.match(/[0-9]/)) {
e.preventDefault();
alert('ФИО директора не должно содержать цифры');
return false;
}
if (inn && !/^\d{10,12}$/.test(inn)) {
e.preventDefault();
alert('ИНН должен содержать 10 или 12 цифр');
return false;
}
const rating = parseFloat(document.getElementById('rating').value);
if (rating < 0 || rating > 5) {
e.preventDefault();
alert('Рейтинг должен быть от 0.0 до 5.0');
return false;
}
const email = document.getElementById('email').value;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
e.preventDefault();
alert('Введите корректный email адрес');
return false;
}
});
</script>
{% endblock %}
{% endblock %}