feat
This commit is contained in:
parent
2d1f17d9d5
commit
092989646b
@ -7,130 +7,345 @@
|
||||
|
||||
{% block title %}Где картины{% endblock %}
|
||||
|
||||
|
||||
{% macro render_doc(doc) %}
|
||||
<figure class="c_4b1hy b_uu2ub" data-exhibition_id="{{doc.get('exhibition_id')}}" data-delivery-id="{{doc._id}}" data-sender="{{doc.sender._id}}" data-receiver="{{doc.receiver._id}}">
|
||||
<img class="b_1fvkt" src="/b/posts/{{doc.img}}">
|
||||
<figcaption class="c_3g8gh b_pvw5a" data-id="{{doc._id}}">
|
||||
{% if doc.status == 'Ожидает отправки' %}
|
||||
<div class="b_thlt1">
|
||||
<span class="b_5dr47">{{doc.status}}</span>
|
||||
<figcaption class="c_3g8gh b_pvw5a" data-id="{{doc._id}}">
|
||||
{% if doc.status == 'Ожидает отправки' %}
|
||||
<div class="b_thlt1">
|
||||
<span class="b_5dr47">{{doc.status}}</span>
|
||||
</div>
|
||||
{% elif doc.status == 'Получено' %}
|
||||
<div class="b_thlt1">
|
||||
<span class="b_5dr47">{{doc.status}}</span>
|
||||
</div>
|
||||
{% elif doc.status == 'Ожидает получения' %}
|
||||
<div class="b_thlt1_2">
|
||||
<span class="b_5dr47_2">{{doc.status}}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="b_thlt1_2">
|
||||
<span class="b_5dr47_2">{{doc.status}}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a class="b_6spaf" href="/profile/delivery/{{doc.post_id}}#main">Посмотреть историю</a>
|
||||
<h2 class="c_9hvtd b_f5fb2">{{doc.title}}</h2>
|
||||
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') %}
|
||||
<p class="b_6spaf">{{doc.get('exhibition').title}}</p>
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.receiver._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Организатор</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
</div>
|
||||
{% elif doc.status == 'Получено' %}
|
||||
<div class="b_thlt1">
|
||||
<span class="b_5dr47">{{doc.status}}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if doc.address %}
|
||||
<p class="b_6spaf">Адрес: {{doc.address}}</p>
|
||||
{% endif %}
|
||||
{% if doc.back_address %}
|
||||
<p class="b_6spaf">Адрес для возврата: {{doc.back_address}}</p>
|
||||
{% endif %}
|
||||
{% if doc.delivery_type %}
|
||||
<p class="b_6spaf">Способ доставки: {{doc.delivery_type}}</p>
|
||||
{% endif %}
|
||||
{% if doc.track %}
|
||||
<p class="b_6spaf">Трэк-номер: {{doc.track}}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.sender %}
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.sender._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Отправитель</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.sender.username}}</span>
|
||||
</div>
|
||||
{% elif doc.status == 'Ожидает получения' %}
|
||||
<div class="b_thlt1_2">
|
||||
<span class="b_5dr47_2">{{doc.status}}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.receiver %}
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.receiver._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Получатель</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
<span class="b_qla2q">+7 *** *** {{ doc.receiver.phone[-4:] }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="b_thlt1_2">
|
||||
<span class="b_5dr47_2">{{doc.status}}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a class="b_6spaf" href="/profile/delivery/{{doc.post_id}}#main">Посмотреть историю</a>
|
||||
<h2 class="c_9hvtd b_f5fb2">{{doc.title}}</h2>
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') %}
|
||||
<p class="b_6spaf">{{doc.get('exhibition').title}}</p>
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.receiver._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Организатор</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if doc.address %}
|
||||
<p class="b_6spaf">Адрес: {{doc.address}}</p>
|
||||
{% endif %}
|
||||
{% if doc.back_address %}
|
||||
<p class="b_6spaf">Адрес для возврата: {{doc.back_address}}</p>
|
||||
{% endif %}
|
||||
{% if doc.delivery_type %}
|
||||
<p class="b_6spaf">Способ доставки: {{doc.delivery_type}}</p>
|
||||
{% endif %}
|
||||
{% if doc.track %}
|
||||
<p class="b_6spaf">Трэк-номер: {{doc.track}}</p>
|
||||
{% endif %}
|
||||
{% if doc.sender %}
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.sender._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Отправитель</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.sender.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if doc.receiver %}
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.receiver._id}}" class="b_ob59y">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<span class="b_qla2q">Получатель</span>
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
<span class="b_qla2q">+7 *** *** {{ doc.receiver.phone[-4:] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if doc.status in ['Ожидает отправки', 'Возврат художнику'] and user._id == doc.sender._id%}
|
||||
<button class="b_wy3rt ba" onclick="btnSend(this, '{{doc._id}}', '{{doc.sender._id}}', '{{doc.receiver._id}}')">
|
||||
<span class="b_bvl0d">Отправить</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if doc.status == 'Ожидает получения' and user._id == doc.receiver._id%}
|
||||
<button class="b_wy3rt ba" onclick="btnAccept(this, '{{doc._id}}')">
|
||||
<span class="b_bvl0d">Получить</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if doc.status == 'Получено' %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p class="b_6spaf">
|
||||
{{ (doc._id.generation_time.strftime('%d.%m.%Y')) if doc._id else ''}}
|
||||
<b>{{ '%02d' % ((doc._id.generation_time.hour + 3) % 24) if doc._id else '' }}:{{ '%02d' % doc._id.generation_time.minute if doc._id else '' }}</b>
|
||||
</p>
|
||||
</figcaption>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Checkbox instead of per-item button #}
|
||||
{% if doc.status in ['Ожидает отправки', 'Возврат художнику'] and user._id == doc.sender._id %}
|
||||
<label class="offer-label delivery-checkbox-label">
|
||||
<input type="checkbox"
|
||||
class="offer-checkbox delivery-checkbox send-checkbox"
|
||||
data-id="{{doc._id}}"
|
||||
data-sender="{{doc.sender._id}}"
|
||||
data-receiver="{{doc.receiver._id}}">
|
||||
<span>Выбрать для отправки</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.status == 'Ожидает получения' and user._id == doc.receiver._id %}
|
||||
<label class="offer-label delivery-checkbox-label">
|
||||
<input type="checkbox"
|
||||
class="offer-checkbox delivery-checkbox accept-checkbox"
|
||||
data-id="{{doc._id}}">
|
||||
<span>Выбрать для получения</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<p class="b_6spaf">
|
||||
{{ (doc._id.generation_time.strftime('%d.%m.%Y')) if doc._id else ''}}
|
||||
<b>{{ '%02d' % ((doc._id.generation_time.hour + 3) % 24) if doc._id else '' }}:{{ '%02d' % doc._id.generation_time.minute if doc._id else '' }}</b>
|
||||
</p>
|
||||
</figcaption>
|
||||
</figure>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{{ header.section(user) }}
|
||||
<section class="c_dg3k4">
|
||||
<div class="c_gtoe9">
|
||||
{{ profile_card.profile_card(user) }}
|
||||
<div class="c_ru432">
|
||||
{{ profile_nav.profile_nav(user, 'delivery') }}
|
||||
<div class="c_tihtk" id="main">
|
||||
{% set statuses = ['Ожидает отправки', 'Возврат художнику'] %}
|
||||
{% if data | selectattr('status', 'in', statuses) | list | length > 0 %}
|
||||
<article class="c_9fgdh b_ax8vz">
|
||||
<div class="c_z3dpx b_73p10">
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="b_8f3uh">Настройки отправки</h3>
|
||||
</header>
|
||||
<p class="b_tfnz2">Можно менять перед каждой отправкой картины</p>
|
||||
<div class="b_73p10">
|
||||
<div class="c_aihm6 floating-group">
|
||||
<select class="c_xhd5k" type="text" id="delivery_type" name="delivery_type" required>
|
||||
<option>СДЭК</option>
|
||||
<option>Самостоятельно</option>
|
||||
</select>
|
||||
<label for="delivery_type" class="c_z3ybu floating-label">Способ доставки</label>
|
||||
</div>
|
||||
<div class="c_aihm6 floating-group">
|
||||
<input placeholder="..." type="text" id="track" name="track" class="c_xhd5k" required
|
||||
data-toast="Поле Трэк-номер должно быть заполнено">
|
||||
<label for="track" class="c_z3ybu floating-label">Трэк-номер</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% for doc in data %}
|
||||
{{ render_doc(doc) }}
|
||||
{% endfor %}
|
||||
<div class="c_gtoe9">
|
||||
{{ profile_card.profile_card(user) }}
|
||||
<div class="c_ru432">
|
||||
{{ profile_nav.profile_nav(user, 'delivery') }}
|
||||
<div class="c_tihtk" id="main">
|
||||
|
||||
{% set statuses = ['Ожидает отправки', 'Возврат художнику'] %}
|
||||
{% if data | selectattr('status', 'in', statuses) | list | length > 0 %}
|
||||
<article class="c_9fgdh b_ax8vz">
|
||||
<div class="c_z3dpx b_73p10">
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="b_8f3uh">Настройки отправки</h3>
|
||||
</header>
|
||||
<p class="b_tfnz2">Можно менять перед каждой отправкой картины</p>
|
||||
<div class="b_73p10">
|
||||
<div class="c_aihm6 floating-group">
|
||||
<select class="c_xhd5k" id="delivery_type" name="delivery_type" required>
|
||||
<option>СДЭК</option>
|
||||
<option>Самостоятельно</option>
|
||||
</select>
|
||||
<label for="delivery_type" class="c_z3ybu floating-label">Способ доставки</label>
|
||||
</div>
|
||||
<div class="c_aihm6 floating-group" id="track-group">
|
||||
<input placeholder="..." type="text" id="track" name="track" class="c_xhd5k" required
|
||||
data-toast="Поле Трэк-номер должно быть заполнено">
|
||||
<label for="track" class="c_z3ybu floating-label">Трэк-номер</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% for doc in data %}
|
||||
{{ render_doc(doc) }}
|
||||
{% endfor %}
|
||||
|
||||
{# Sticky bottom action bar #}
|
||||
<div class="st" style="z-index: 2000;min-height: 101px;padding: 25px;gap: 5px;">
|
||||
<button class="b_wy3rt ba" id="btnSendSelected" style="display: none;" onclick="btnSendSelected()">
|
||||
<span class="b_bvl0d">Отправить выбранные</span>
|
||||
</button>
|
||||
<button class="b_wy3rt ba" id="btnAcceptSelected" style="display: none;" onclick="btnAcceptSelected()">
|
||||
<span class="b_bvl0d">Получить выбранные</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Collect selected IDs
|
||||
function getSelectedSendIds() {
|
||||
return Array.from(document.querySelectorAll('.send-checkbox:checked')).map(cb => ({
|
||||
id: cb.dataset.id,
|
||||
sender: cb.dataset.sender,
|
||||
receiver: cb.dataset.receiver
|
||||
}));
|
||||
}
|
||||
|
||||
function getSelectedAcceptIds() {
|
||||
return Array.from(document.querySelectorAll('.accept-checkbox:checked')).map(cb => cb.dataset.id);
|
||||
}
|
||||
|
||||
// Show / hide sticky buttons depending on selection
|
||||
function updateStickyButtons() {
|
||||
const sendCount = document.querySelectorAll('.send-checkbox:checked').length;
|
||||
const acceptCount = document.querySelectorAll('.accept-checkbox:checked').length;
|
||||
|
||||
const sendBtn = document.getElementById('btnSendSelected');
|
||||
const acceptBtn = document.getElementById('btnAcceptSelected');
|
||||
|
||||
if (sendBtn) sendBtn.style.display = sendCount > 0 ? 'inline-flex' : 'none';
|
||||
if (acceptBtn) acceptBtn.style.display = acceptCount > 0 ? 'inline-flex' : 'none';
|
||||
}
|
||||
|
||||
// Listen to checkbox changes
|
||||
document.addEventListener('change', (e) => {
|
||||
if (e.target.classList.contains('delivery-checkbox')) {
|
||||
updateStickyButtons();
|
||||
}
|
||||
});
|
||||
|
||||
// Bulk Accept
|
||||
async function btnAcceptSelected() {
|
||||
const ids = getSelectedAcceptIds();
|
||||
if (!ids.length) return;
|
||||
|
||||
for (const itemId of ids) {
|
||||
const checkbox = document.querySelector(`.accept-checkbox[data-id="${itemId}"]`);
|
||||
const figcaption = checkbox.closest('figcaption');
|
||||
const statusDiv = figcaption.querySelectorAll('div')[0];
|
||||
const statusSpan = statusDiv.querySelector('span');
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/delivery/accept', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ item: itemId })
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let errorMessage = 'Ошибка сохранения';
|
||||
try {
|
||||
const errorData = await res.json();
|
||||
errorMessage = errorData.detail || errorMessage;
|
||||
} catch (e) {
|
||||
try {
|
||||
const errorText = await res.text();
|
||||
errorMessage = errorText || errorMessage;
|
||||
} catch (e) {}
|
||||
}
|
||||
showToast({ message: errorMessage, duration: 2500 });
|
||||
continue;
|
||||
}
|
||||
|
||||
statusDiv.className = 'b_thlt1';
|
||||
statusSpan.className = 'b_5dr47';
|
||||
statusSpan.textContent = 'Получено';
|
||||
checkbox.closest('label').style.display = 'none';
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showToast({ message: 'Не удалось подключиться к серверу', duration: 2500 });
|
||||
}
|
||||
}
|
||||
updateStickyButtons();
|
||||
}
|
||||
|
||||
// Bulk Send
|
||||
async function btnSendSelected() {
|
||||
const items = getSelectedSendIds();
|
||||
if (!items.length) return;
|
||||
|
||||
const delivery_type = document.getElementById('delivery_type')?.value || '';
|
||||
const trackValue = document.getElementById('track');
|
||||
let track = '';
|
||||
if (delivery_type === 'СДЭК' && trackValue) {
|
||||
track = trackValue.value;
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
const requiredInputs = document.querySelectorAll('input[required]');
|
||||
const requiredTextareas = document.querySelectorAll('textarea[required]');
|
||||
const requiredFields = [...requiredInputs, ...requiredTextareas];
|
||||
let firstErrorField = null;
|
||||
let hasError = false;
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const group = field.closest('.floating-group');
|
||||
if (!group) return;
|
||||
let isEmpty = false;
|
||||
if (field.type === 'file') {
|
||||
isEmpty = !field.files || field.files.length === 0;
|
||||
} else {
|
||||
isEmpty = !field.value || !field.value.trim();
|
||||
}
|
||||
if (isEmpty) {
|
||||
group.classList.add('error');
|
||||
hasError = true;
|
||||
showToast({ message: field.dataset.toast, duration: 2500 });
|
||||
if (!firstErrorField) firstErrorField = field;
|
||||
} else {
|
||||
group.classList.remove('error');
|
||||
}
|
||||
});
|
||||
|
||||
if (hasError && firstErrorField) {
|
||||
firstErrorField.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
setTimeout(() => firstErrorField.focus(), 600);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const { id: itemId, sender, receiver: too } of items) {
|
||||
const checkbox = document.querySelector(`.send-checkbox[data-id="${itemId}"]`);
|
||||
const figcaption = checkbox.closest('figcaption');
|
||||
const statusDiv = figcaption.querySelectorAll('div')[0];
|
||||
const statusSpan = statusDiv.querySelector('span');
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/delivery/send', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sender,
|
||||
too,
|
||||
item: itemId,
|
||||
delivery_type,
|
||||
track
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let errorMessage = 'Ошибка сохранения';
|
||||
try {
|
||||
const errorData = await res.json();
|
||||
errorMessage = errorData.detail || errorMessage;
|
||||
} catch (e) {
|
||||
try {
|
||||
const errorText = await res.text();
|
||||
errorMessage = errorText || errorMessage;
|
||||
} catch (e) {}
|
||||
}
|
||||
showToast({ message: errorMessage, duration: 2500 });
|
||||
continue;
|
||||
}
|
||||
|
||||
statusDiv.className = 'b_thlt1_2';
|
||||
statusSpan.className = 'b_5dr47_2';
|
||||
statusSpan.textContent = 'Ожидает получения';
|
||||
checkbox.closest('label').style.display = 'none';
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showToast({ message: 'Не удалось подключиться к серверу', duration: 2500 });
|
||||
}
|
||||
}
|
||||
updateStickyButtons();
|
||||
}
|
||||
</script>
|
||||
<script defer>
|
||||
function toggleTrackField() {
|
||||
const deliveryType = document.getElementById('delivery_type');
|
||||
const trackGroup = document.getElementById('track-group');
|
||||
const trackInput = document.getElementById('track');
|
||||
|
||||
if (!deliveryType || !trackGroup || !trackInput) return;
|
||||
|
||||
if (deliveryType.value === 'Самостоятельно') {
|
||||
trackGroup.style.display = 'none';
|
||||
trackInput.removeAttribute('required');
|
||||
trackInput.value = ''; // clear value when hidden
|
||||
} else {
|
||||
trackGroup.style.display = '';
|
||||
trackInput.setAttribute('required', '');
|
||||
}
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
document.addEventListener('DOMContentLoaded', toggleTrackField);
|
||||
|
||||
// Run every time the select changes
|
||||
document.getElementById('delivery_type')?.addEventListener('change', toggleTrackField);
|
||||
const query = window.location.href.split('?')[1] || '';
|
||||
let exhibition_id = null;
|
||||
if (query) {
|
||||
@ -174,132 +389,7 @@
|
||||
deliveryType.addEventListener('change', toggleTrack);
|
||||
toggleTrack();
|
||||
</script>
|
||||
<script>
|
||||
async function btnAccept(btn, itemId){
|
||||
const figcaption = btn.parentElement;
|
||||
const statusDiv = figcaption.querySelectorAll('div')[0];
|
||||
const statusSpan = statusDiv.querySelector('span')
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/delivery/accept', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
item: itemId,
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let errorMessage = 'Ошибка сохранения';
|
||||
try {
|
||||
const errorData = await res.json();
|
||||
errorMessage = errorData.detail || errorMessage;
|
||||
} catch (e) {
|
||||
try {
|
||||
const errorText = await res.text();
|
||||
errorMessage = errorText || errorMessage;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
showToast({ message: errorMessage, duration: 2500 });
|
||||
return;
|
||||
}
|
||||
statusDiv.className = 'b_thlt1';
|
||||
statusSpan.className = 'b_5dr47';
|
||||
statusSpan.textContent = 'Получено';
|
||||
btn.style.display = 'none'
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showToast({ message: 'Не удалось подключиться к серверу', duration: 2500 });
|
||||
}
|
||||
};
|
||||
async function btnSend(btn, itemId, sender, too){
|
||||
const figcaption = btn.parentElement;
|
||||
const statusDiv = figcaption.querySelectorAll('div')[0];
|
||||
const statusSpan = statusDiv.querySelector('span')
|
||||
|
||||
|
||||
const delivery_type = document.getElementById('delivery_type').value;
|
||||
const trackValue = document.getElementById('track');
|
||||
|
||||
let track = ''
|
||||
if (delivery_type === 'СДЭК' && trackValue) {
|
||||
track = trackValue.value
|
||||
}
|
||||
const requiredInputs = document.querySelectorAll('input[required]');
|
||||
const requiredTextareas = document.querySelectorAll('textarea[required]');
|
||||
const requiredFields = [...requiredInputs, ...requiredTextareas];
|
||||
|
||||
let firstErrorField = null;
|
||||
let hasError = false;
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const group = field.closest('.floating-group');
|
||||
if (!group) return; // skip if no group wrapper
|
||||
|
||||
let isEmpty = false;
|
||||
if (field.type === 'file') {
|
||||
isEmpty = !field.files || field.files.length === 0;
|
||||
} else {
|
||||
isEmpty = !field.value || !field.value.trim();
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
group.classList.add('error');
|
||||
hasError = true;
|
||||
showToast({message: field.dataset.toast, duration:2500});
|
||||
if (!firstErrorField) firstErrorField = field;
|
||||
} else {
|
||||
group.classList.remove('error');
|
||||
}
|
||||
});
|
||||
|
||||
if (hasError && firstErrorField) {
|
||||
firstErrorField.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
setTimeout(() => firstErrorField.focus(), 600);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/delivery/send', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sender,
|
||||
too,
|
||||
item: itemId,
|
||||
delivery_type,
|
||||
track: track
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let errorMessage = 'Ошибка сохранения';
|
||||
try {
|
||||
const errorData = await res.json();
|
||||
errorMessage = errorData.detail || errorMessage;
|
||||
} catch (e) {
|
||||
// If response isn't JSON, try to get text
|
||||
try {
|
||||
const errorText = await res.text();
|
||||
errorMessage = errorText || errorMessage;
|
||||
} catch (e) {
|
||||
// Fallback to generic error
|
||||
}
|
||||
}
|
||||
showToast({ message: errorMessage, duration: 2500 });
|
||||
return;
|
||||
}
|
||||
statusDiv.className = 'b_thlt1_2';
|
||||
statusSpan.className = 'b_5dr47_2';
|
||||
statusSpan.textContent = 'Ожидает получения';
|
||||
btn.style.display = 'none'
|
||||
} catch (error) {
|
||||
console.error('Network error:', error);
|
||||
showToast({ message: 'Не удалось подключиться к серверу', duration: 2500 });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user