project2463/templates/edit_client.html

815 lines
30 KiB
HTML
Raw Permalink 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" %}
{% block title %}Редактирование поставщика{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8">
<!-- Хлебные крошки -->
<nav aria-label="breadcrumb" class="mb-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('index') }}">Поставщики</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('order_history', client_id=client.id) }}">{{ client.name }}</a></li>
<li class="breadcrumb-item active">Редактирование</li>
</ol>
</nav>
<div class="form-card">
<div class="form-card-header">
<h2 class="form-card-title">
<i class="fas fa-truck"></i> Редактирование поставщика
</h2>
</div>
<div class="form-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">
<i class="fas fa-{{ 'exclamation-circle' if category == 'error' else 'check-circle' }} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" id="supplierForm">
<div class="row">
<!-- Основная информация -->
<div class="col-md-6">
<h5 class="section-title">
<i class="fas fa-info-circle"></i> Основная информация
</h5>
<div class="form-group">
<label for="name" class="form-label">
<i class="fas fa-building"></i>
Название компании
</label>
<input type="text" class="form-control modern-input" id="name" name="name"
value="{{ client.name }}" required maxlength="255"
onkeypress="return blockNumbers(event)" onpaste="return blockNumbersPaste(event)">
</div>
<div class="form-group">
<label for="director" class="form-label">
<i class="fas fa-user-tie"></i>
Директор (ФИО)
</label>
<input type="text" class="form-control modern-input" id="director" name="director"
value="{{ client.director or '' }}" maxlength="255"
onkeypress="return blockNumbers(event)" onpaste="return blockNumbersPaste(event)">
</div>
<div class="form-group">
<label for="contractor_type_id" class="form-label">
<i class="fas fa-tag"></i>
Тип поставщика
</label>
<div class="select-wrapper">
<select class="form-select modern-select" id="contractor_type_id" name="contractor_type_id" required>
<option value="" disabled>Выберите тип поставщика</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>
<i class="fas fa-chevron-down select-arrow"></i>
</div>
</div>
<div class="form-group">
<label for="rating" class="form-label">
<i class="fas fa-star"></i>
Рейтинг надежности
</label>
<input type="number" class="form-control modern-input" 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="section-title">
<i class="fas fa-address-card"></i> Контактные данные
</h5>
<div class="form-group">
<label for="email" class="form-label">
<i class="fas fa-envelope"></i>
Email
</label>
<input type="email" class="form-control modern-input" id="email" name="email"
value="{{ client.email }}" required maxlength="255">
</div>
<div class="form-group">
<label for="phone" class="form-label">
<i class="fas fa-phone"></i>
Телефон
</label>
<div class="phone-input-container">
<input type="tel" class="form-control modern-input" id="phone" name="phone"
value="{{ client.phone or '' }}" maxlength="18"
onkeypress="return allowPhoneCharacters(event)"
onkeyup="formatPhoneNumber(this)"
onblur="validatePhoneNumber(this)"
placeholder="+7 (999) 999-99-99">
<i class="fas fa-phone phone-icon"></i>
</div>
<div class="phone-hint" id="phoneHint">
<i class="fas fa-info-circle"></i> Формат: +7 (XXX) XXX-XX-XX
</div>
<div class="phone-error" id="phoneError" style="display: none;">
<i class="fas fa-exclamation-circle"></i>
<span id="phoneErrorMessage"></span>
</div>
</div>
<div class="form-group">
<label for="inn" class="form-label">
<i class="fas fa-id-card"></i>
ИНН
</label>
<input type="text" class="form-control modern-input" id="inn" name="inn"
value="{{ client.inn or '' }}" maxlength="12"
onkeypress="return allowOnlyNumbers(event)" onpaste="return allowNumbersPaste(event)">
<div class="form-text">10 или 12 цифр</div>
</div>
</div>
<!-- Адрес на всю ширину -->
<div class="col-12">
<div class="form-group">
<label for="address" class="form-label">
<i class="fas fa-map-marker-alt"></i>
Юридический адрес
</label>
<textarea class="form-control modern-input" id="address" name="address"
rows="3" maxlength="500">{{ client.address or '' }}</textarea>
</div>
</div>
</div>
<!-- Кнопки -->
<div class="form-actions">
<div>
<a href="{{ url_for('index') }}" class="btn btn-outline">
<i class="fas fa-times"></i> Отмена
</a>
</div>
<div class="d-flex gap-2">
<a href="{{ url_for('order_history', client_id=client.id) }}"
class="btn btn-info">
<i class="fas fa-history"></i> История заказов
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Сохранить
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
/* ===== ОСНОВНЫЕ ПЕРЕМЕННЫЕ ===== */
:root {
--antique-white: #FAEBD7;
--light-brown: #DEB887;
--brown: #A0522D;
--dark-brown: #8B4513;
--error-red: #dc3545;
--success-green: #28a745;
}
/* ===== ХЛЕБНЫЕ КРОШКИ ===== */
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item a {
color: var(--brown);
text-decoration: none;
font-family: 'Verdana', sans-serif;
}
.breadcrumb-item a:hover {
text-decoration: underline;
}
.breadcrumb-item.active {
color: #6c757d;
font-family: 'Verdana', sans-serif;
}
.breadcrumb-item + .breadcrumb-item::before {
color: var(--light-brown);
}
/* ===== КАРТОЧКА ФОРМЫ ===== */
.form-card {
background: white;
border: 1px solid var(--light-brown);
border-radius: 16px;
box-shadow: 0 4px 20px rgba(160, 82, 45, 0.1);
overflow: hidden;
}
.form-card-header {
background: linear-gradient(135deg, var(--brown) 0%, var(--dark-brown) 100%);
padding: 20px 25px;
color: white;
}
.form-card-title {
font-size: 1.4rem;
font-weight: 600;
font-family: 'Verdana', sans-serif;
margin: 0;
}
.form-card-title i {
margin-right: 10px;
color: white;
}
.form-card-body {
padding: 25px;
}
/* ===== ЗАГОЛОВКИ СЕКЦИЙ ===== */
.section-title {
color: var(--brown);
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--light-brown);
font-family: 'Verdana', sans-serif;
}
.section-title i {
color: var(--brown);
margin-right: 8px;
}
/* ===== ЭЛЕМЕНТЫ ФОРМЫ ===== */
.form-group {
margin-bottom: 20px;
}
.form-label {
display: flex;
align-items: center;
gap: 6px;
color: var(--brown);
font-weight: 600;
font-size: 0.95rem;
margin-bottom: 6px;
font-family: 'Verdana', sans-serif;
}
.form-label i {
color: var(--brown);
font-size: 1rem;
}
.modern-input {
width: 100%;
height: 42px;
padding: 0 12px;
border: 2px solid var(--light-brown);
border-radius: 10px;
font-size: 0.95rem;
font-family: 'Verdana', sans-serif;
transition: all 0.2s ease;
background: white;
}
.modern-input:focus {
outline: none;
border-color: var(--brown);
box-shadow: 0 0 0 3px rgba(160, 82, 45, 0.1);
}
.modern-input:hover {
border-color: var(--brown);
}
textarea.modern-input {
height: auto;
padding: 10px 12px;
resize: vertical;
}
/* Специальные стили для поля телефона */
.phone-input-container {
position: relative;
}
.phone-input-container .modern-input {
padding-left: 40px;
}
.phone-icon {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--light-brown);
pointer-events: none;
transition: color 0.2s ease;
}
.phone-input-container .modern-input:focus + .phone-icon {
color: var(--brown);
}
.phone-hint {
font-size: 0.8rem;
color: #6c757d;
margin-top: 4px;
font-family: 'Verdana', sans-serif;
display: flex;
align-items: center;
gap: 4px;
}
.phone-hint i {
color: var(--light-brown);
font-size: 0.75rem;
}
.phone-error {
font-size: 0.8rem;
color: var(--error-red);
margin-top: 4px;
font-family: 'Verdana', sans-serif;
display: flex;
align-items: center;
gap: 4px;
}
.phone-error i {
color: var(--error-red);
font-size: 0.75rem;
}
.select-wrapper {
position: relative;
}
.modern-select {
width: 100%;
height: 42px;
padding: 0 35px 0 12px;
border: 2px solid var(--light-brown);
border-radius: 10px;
font-size: 0.95rem;
font-family: 'Verdana', sans-serif;
background: white;
cursor: pointer;
appearance: none;
transition: all 0.2s ease;
}
.modern-select:focus {
outline: none;
border-color: var(--brown);
box-shadow: 0 0 0 3px rgba(160, 82, 45, 0.1);
}
.modern-select:hover {
border-color: var(--brown);
}
.select-arrow {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--brown);
pointer-events: none;
}
.form-text {
font-size: 0.8rem;
color: #6c757d;
margin-top: 4px;
font-family: 'Verdana', sans-serif;
}
/* ===== КНОПКИ ===== */
.form-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 25px;
padding-top: 20px;
border-top: 2px solid var(--light-brown);
}
.btn {
padding: 10px 20px;
border-radius: 10px;
font-size: 0.95rem;
font-weight: 500;
font-family: 'Verdana', sans-serif;
transition: all 0.2s ease;
border: none;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
}
.btn-primary {
background: var(--brown);
color: white;
}
.btn-primary:hover {
background: var(--dark-brown);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(160, 82, 45, 0.3);
}
.btn-info {
background: var(--light-brown);
color: #2c3e50;
}
.btn-info:hover {
background: var(--brown);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(160, 82, 45, 0.3);
}
.btn-outline {
background: white;
border: 2px solid var(--light-brown);
color: var(--brown);
}
.btn-outline:hover {
background: var(--antique-white);
border-color: var(--brown);
transform: translateY(-2px);
}
.d-flex.gap-2 {
gap: 10px;
}
/* ===== АЛЕРТЫ ===== */
.alert {
border-radius: 10px;
border: 1px solid var(--light-brown);
font-family: 'Verdana', sans-serif;
margin-bottom: 20px;
}
.alert-success {
background: var(--antique-white);
color: var(--brown);
border-left: 4px solid #28a745;
}
.alert-danger {
background: var(--antique-white);
color: #721c24;
border-left: 4px solid #dc3545;
}
/* ===== ИКОНКИ ===== */
i {
color: var(--brown);
}
.btn-primary i, .btn-info i, .btn-outline i {
color: inherit;
}
/* ===== АДАПТИВНОСТЬ ===== */
@media (max-width: 768px) {
.form-card-body {
padding: 20px;
}
.form-actions {
flex-direction: column-reverse;
gap: 10px;
}
.form-actions > div {
width: 100%;
}
.d-flex.gap-2 {
flex-direction: column;
width: 100%;
}
.d-flex.gap-2 .btn {
width: 100%;
justify-content: center;
}
.btn-outline {
width: 100%;
justify-content: center;
}
.col-md-6 {
width: 100%;
}
.section-title {
margin-top: 15px;
}
.section-title:first-of-type {
margin-top: 0;
}
}
</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();
alert('Цифры запрещены в этом поле');
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]/)) {
alert('Цифры были удалены из вставленного текста');
}
}
// Разрешаем только цифры (для ИНН)
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();
alert('Только цифры разрешены в этом поле');
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]/)) {
alert('Были вставлены только цифры, остальные символы удалены');
}
}
// Для поля телефона разрешаем цифры и необходимые символы
function allowPhoneCharacters(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 formatPhoneNumber(input) {
let value = input.value.replace(/\D/g, '');
// Если номер начинается с 8 или 7, заменяем на +7
if (value.length > 0) {
if (value[0] === '8') {
value = '7' + value.substring(1);
}
// Форматируем номер
if (value.length <= 11) {
let formattedValue = '';
if (value.length > 0) {
formattedValue = '+7';
if (value.length > 1) {
formattedValue += ' (' + value.substring(1, Math.min(4, value.length));
if (value.length >= 4) {
formattedValue += ') ';
if (value.length >= 7) {
formattedValue += value.substring(4, 7);
if (value.length >= 9) {
formattedValue += '-' + value.substring(7, 9);
if (value.length >= 11) {
formattedValue += '-' + value.substring(9, 11);
}
}
} else if (value.length > 4) {
formattedValue += value.substring(4);
}
}
}
}
input.value = formattedValue;
}
}
// Проверяем валидность номера
validatePhoneNumber(input);
}
// Валидация номера телефона
function validatePhoneNumber(input) {
const phoneValue = input.value.replace(/\D/g, '');
const phoneError = document.getElementById('phoneError');
const phoneErrorMessage = document.getElementById('phoneErrorMessage');
const phoneHint = document.getElementById('phoneHint');
if (phoneValue.length === 0) {
// Пустое поле - допустимо
phoneError.style.display = 'none';
phoneHint.style.display = 'flex';
input.style.borderColor = 'var(--light-brown)';
return true;
}
if (phoneValue.length === 11 && phoneValue[0] === '7') {
// Валидный номер
phoneError.style.display = 'none';
phoneHint.style.display = 'flex';
input.style.borderColor = 'var(--success-green)';
return true;
} else {
// Невалидный номер
phoneError.style.display = 'flex';
phoneHint.style.display = 'none';
input.style.borderColor = 'var(--error-red)';
if (phoneValue.length !== 11) {
phoneErrorMessage.textContent = 'Номер должен содержать 11 цифр (включая код страны)';
} else if (phoneValue[0] !== '7') {
phoneErrorMessage.textContent = 'Номер должен начинаться с 7 или 8';
}
return false;
}
}
// Валидация формы
document.getElementById('supplierForm').addEventListener('submit', function(e) {
const inn = document.getElementById('inn').value;
const name = document.getElementById('name').value;
const director = document.getElementById('director').value;
const phone = document.getElementById('phone').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;
}
// Проверка телефона
if (phone) {
const phoneDigits = phone.replace(/\D/g, '');
if (phoneDigits.length > 0) {
if (phoneDigits.length !== 11 || phoneDigits[0] !== '7') {
e.preventDefault();
alert('Пожалуйста, введите корректный номер телефона в формате: +7 (XXX) XXX-XX-XX');
return false;
}
}
}
// Проверка рейтинга
const rating = parseFloat(document.getElementById('rating').value);
if (rating < 0 || rating > 5) {
e.preventDefault();
alert('Рейтинг должен быть от 0.0 до 5.0');
return false;
}
// Проверка email
const email = document.getElementById('email').value;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
e.preventDefault();
alert('Введите корректный email адрес');
return false;
}
});
// Форматирование телефона при загрузке страницы
window.addEventListener('load', function() {
const phoneInput = document.getElementById('phone');
if (phoneInput.value) {
formatPhoneNumber(phoneInput);
}
});
</script>
{% endblock %}
{% endblock %}