171 lines
8.9 KiB
HTML
171 lines
8.9 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block content %}
|
||
<h2>Управление заказами</h2>
|
||
|
||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||
{% if messages %}
|
||
{% for category, message in messages %}
|
||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% endwith %}
|
||
|
||
<div class="filters">
|
||
<h3>Фильтры и сортировка</h3>
|
||
<div class="filter-bar">
|
||
<label>Сортировка:</label>
|
||
<select onchange="window.location.href='?sort='+this.value+'&status={{ status }}'">
|
||
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>Новые сначала</option>
|
||
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>Старые сначала</option>
|
||
<option value="price_high" {% if sort == 'price_high' %}selected{% endif %}>Дорогие сначала</option>
|
||
<option value="price_low" {% if sort == 'price_low' %}selected{% endif %}>Дешевые сначала</option>
|
||
</select>
|
||
<label>Статус:</label>
|
||
<select onchange="window.location.href='?sort={{ sort }}&status='+this.value">
|
||
<option value="all" {% if status == 'all' %}selected{% endif %}>Все</option>
|
||
<option value="pending" {% if status == 'pending' %}selected{% endif %}>Ожидает</option>
|
||
<option value="confirmed" {% if status == 'confirmed' %}selected{% endif %}>Подтвержден</option>
|
||
<option value="completed" {% if status == 'completed' %}selected{% endif %}>Выполнен</option>
|
||
<option value="cancelled" {% if status == 'cancelled' %}selected{% endif %}>Отменен</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="admin-table">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Пользователь</th>
|
||
<th>Услуга</th>
|
||
<th>Дата</th>
|
||
<th>Время</th>
|
||
<th>Цена</th>
|
||
<th>Скидка</th>
|
||
<th>Оплата</th>
|
||
<th>Статус</th>
|
||
<th>Сообщения</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="orders-table-body">
|
||
{% for order in orders %}
|
||
<tr id="order-row-{{ order.id }}">
|
||
<td>{{ order.id }}</td>
|
||
<td>{{ order.username or 'Гость' }}</td>
|
||
<td>{{ order.service_name }}</td>
|
||
<td>{{ order.date or '-' }}</td>
|
||
<td>{{ order.time or '-' }}</td>
|
||
<td>{{ order.price }} руб.</td>
|
||
<td>
|
||
{% if order.discount_applied %}
|
||
<span style="color: #28a745;">{{ order.discount_amount }} руб.</span>
|
||
{% else %}
|
||
—
|
||
{% endif %}
|
||
</td>
|
||
<td class="payment-status-cell" data-order-id="{{ order.id }}">
|
||
{% if order.payment_status == 'paid' %}
|
||
<span style="color: #28a745;">✅ Оплачено</span>
|
||
{% else %}
|
||
<span style="color: #ffc107;">⚠️ Не оплачено</span>
|
||
{% endif %}
|
||
<button class="toggle-payment-btn" data-order-id="{{ order.id }}" style="background: none; border: none; cursor: pointer; margin-left: 5px;">🔄</button>
|
||
</td>
|
||
<td>
|
||
<span class="status status-{{ order.status }}">
|
||
{% if order.status == 'pending' %}Ожидает
|
||
{% elif order.status == 'confirmed' %}Подтвержден
|
||
{% elif order.status == 'completed' %}Выполнен
|
||
{% elif order.status == 'cancelled' %}Отменен
|
||
{% endif %}
|
||
</span>
|
||
</td>
|
||
<td class="unread-cell" data-order-id="{{ order.id }}">
|
||
{% if order.unread_count > 0 %}
|
||
<span class="unread-badge" style="background: #dc3545; color: white; border-radius: 50%; padding: 2px 8px; font-size: 12px;">{{ order.unread_count }}</span>
|
||
{% else %}
|
||
—
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<a href="{{ url_for('chat', order_id=order.id) }}" class="chat-link btn-primary btn-sm" data-order-id="{{ order.id }}" style="border-radius: 50px; padding: 0.3rem 0.8rem; text-decoration: none;">Чат</a>
|
||
<form method="POST" action="{{ url_for('update_order', order_id=order.id) }}?sort={{ sort }}&status={{ status }}" style="display: inline;">
|
||
<select name="status" onchange="this.form.submit()" style="border-radius: 50px; padding: 0.3rem;">
|
||
<option value="pending" {% if order.status == 'pending' %}selected{% endif %}>Ожидает</option>
|
||
<option value="confirmed" {% if order.status == 'confirmed' %}selected{% endif %}>Подтвержден</option>
|
||
<option value="completed" {% if order.status == 'completed' %}selected{% endif %}>Выполнен</option>
|
||
<option value="cancelled" {% if order.status == 'cancelled' %}selected{% endif %}>Отменен</option>
|
||
</select>
|
||
</form>
|
||
<form method="POST" action="{{ url_for('delete_order', order_id=order.id) }}?sort={{ sort }}&status={{ status }}" style="display: inline;" onsubmit="return confirm('Удалить заказ?')">
|
||
<button type="submit" class="btn-danger btn-sm" style="border-radius: 50px; padding: 0.3rem 0.8rem;">Удалить</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<script>
|
||
// Переключение статуса оплаты
|
||
document.querySelectorAll('.toggle-payment-btn').forEach(btn => {
|
||
btn.addEventListener('click', async function() {
|
||
const orderId = this.dataset.orderId;
|
||
const response = await fetch('/api/toggle_payment', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ order_id: orderId })
|
||
});
|
||
const data = await response.json();
|
||
const cell = document.querySelector(`.payment-status-cell[data-order-id="${orderId}"]`);
|
||
if (data.status === 'paid') {
|
||
cell.innerHTML = '<span style="color: #28a745;">✅ Оплачено</span> <button class="toggle-payment-btn" data-order-id="' + orderId + '" style="background: none; border: none; cursor: pointer; margin-left: 5px;">🔄</button>';
|
||
} else {
|
||
cell.innerHTML = '<span style="color: #ffc107;">⚠️ Не оплачено</span> <button class="toggle-payment-btn" data-order-id="' + orderId + '" style="background: none; border: none; cursor: pointer; margin-left: 5px;">🔄</button>';
|
||
}
|
||
document.querySelectorAll('.toggle-payment-btn').forEach(btn => {
|
||
btn.removeEventListener('click', this);
|
||
btn.addEventListener('click', arguments.callee);
|
||
});
|
||
});
|
||
});
|
||
|
||
async function updateUnreadCounts() {
|
||
try {
|
||
const response = await fetch('/api/admin_unread_counts');
|
||
const data = await response.json();
|
||
for (const [orderId, count] of Object.entries(data)) {
|
||
const cell = document.querySelector(`.unread-cell[data-order-id="${orderId}"]`);
|
||
if (cell) {
|
||
if (count > 0) {
|
||
cell.innerHTML = `<span class="unread-badge" style="background: #dc3545; color: white; border-radius: 50%; padding: 2px 8px; font-size: 12px;">${count}</span>`;
|
||
} else {
|
||
cell.innerHTML = '—';
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка обновления:', error);
|
||
}
|
||
}
|
||
|
||
document.querySelectorAll('.chat-link').forEach(link => {
|
||
link.addEventListener('click', async function(e) {
|
||
const orderId = this.dataset.orderId;
|
||
await fetch('/api/mark_order_read', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ order_id: orderId })
|
||
});
|
||
const cell = document.querySelector(`.unread-cell[data-order-id="${orderId}"]`);
|
||
if (cell) cell.innerHTML = '—';
|
||
});
|
||
});
|
||
|
||
setInterval(updateUnreadCounts, 10000);
|
||
</script>
|
||
{% endblock %}
|