This commit is contained in:
vladislav 2026-08-17 10:33:06 -04:00
parent abacc5e152
commit fe8a036f06
Signed by: Vladislav
GPG Key ID: D97C9E5E4860EE45
3 changed files with 47 additions and 218 deletions

View File

@ -4601,13 +4601,14 @@
"\n",
" data = [doc async for doc in cursor]\n",
" exhibitions = await exhibition_collection.find({'_id': {'$in': [ObjectId(i.get('exhibition_id')) for i in data if i.get('exhibition_id')]}}).to_list()\n",
"\n",
" competitions = await competitions_col.find({'_id': {'$in': [i.get('competitions') for i in data if i.get('competitions')]}}).to_list()\n",
" return templates.TemplateResponse(\n",
" request=request,\n",
" name=\"profile/delivery.html\",\n",
" context={\n",
" \"user\": user,\n",
" \"exhibitions\": exhibitions,\n",
" \"competitions\": competitions,\n",
" \"data\": data,\n",
" \"page_title\": \"Доставка\",\n",
" \"active_section\": \"delivery\"\n",

View File

@ -3505,13 +3505,14 @@ async def profile_delivery(
data = [doc async for doc in cursor]
exhibitions = await exhibition_collection.find({'_id': {'$in': [ObjectId(i.get('exhibition_id')) for i in data if i.get('exhibition_id')]}}).to_list()
competitions = await competitions_col.find({'_id': {'$in': [i.get('competitions') for i in data if i.get('competitions')]}}).to_list()
return templates.TemplateResponse(
request=request,
name="profile/delivery.html",
context={
"user": user,
"exhibitions": exhibitions,
"competitions": competitions,
"data": data,
"page_title": "Доставка",
"active_section": "delivery"

View File

@ -4,6 +4,7 @@
{% import "components/profile_card.html" as profile_card %}
{% import "components/profile_nav.html" as profile_nav %}
{% import "competition/list.html" as competition %}
{% import "competition/card.html" as card %}
{% block title %}Где картины{% endblock %}
@ -100,223 +101,48 @@
<div class="c_ru432">
{{ profile_nav.profile_nav(user, 'delivery') }}
<div class="c_tihtk" id="main">
{% set statuses = ['Ожидает отправки', 'Возврат художнику'] %}
<article class="c_9fgdh b_ax8vz">
<div class="c_z3dpx b_73p10">
<header class="c_m876y b_9xtz9">
<h3 class="b_8f3uh">Фильтры</h3>
</header>
<div class="c_aihm6 floating-group">
<select class="c_xhd5k" id="exhibition_id" name="exhibition_id" required
onchange="filterFigure(this.value)">
<option value="">Выбрать выставку</option>
{% for doc in exhibitions %}
<option value="{{doc._id}}">{{doc.title}}</option>
{% for competition in competitions %}
{{card.section(competition, False, "/competition/{}".format(competition._id))}}
{% endfor %}
</select>
<label for="exhibition_id" class="c_z3ybu floating-label">Выставка</label>
{% for i in exhibitions %}
<article class="c_9fgdh b_ax8vz">
<a href="/profile/partner/exhibition/{{i._id}}{% if i.status == 0 %}/catalog{%endif%}#main">
<div class="b_ornr9">
<picture>
<img src="/b/exhibition/{{i.poster_file_id}}" data-src="/b/exhibition/{{i.poster_orig_id}}" id="preview_poster" crossorigin="anonymous" fetchpriority="high" loading="eager" alt="Загрузите афишу" class="b_ornr9">
</picture>
</div>
</a>
<div class="c_z3dpx b_73p10">
<a class="b_73p10" href="/profile/partner/exhibition/{{i._id}}{% if i.status == 0 %}/catalog{%endif%}#main">
<div class="c_c4wam b_vabj9">
{% if now > i.date_finish %}
<span class="c_95csi b_0jst1">Закончилась</span>
{% elif now > i.datetime_start %}
<span class="c_95csi b_0jst1">Началась</span>
{% else %}
<span class="c_95csi b_0jst1">Формируется</span>
{% endif %}
</div>
{% if data | selectattr('status', 'in', statuses) | list | length > 0 %}
<header class="c_m876y b_9xtz9">
<h3 class="b_8f3uh">Настройки отправки</h3>
<h3 class="c_hysjg b_8f3uh" id="preview_title">{{i.title}}</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>
<p class="c_2fr39 b_tfnz2" id="preview_topic">{{i.topic}}</p>
<div class="c_dbbe4 b_otttb">
{% if user %}
<img crossorigin="anonymous" fetchpriority="high" loading="eager" class="c_pm80w b_a68fq" src="/a/{{i.user._id}}" alt="{{ i.user.username | e }}">
<div class="c_4402p b_ntazj">
<p class="c_kvah4 b_ghosy">{{ i.user.username }}</p>
</div>
{% endif %}
</div>
<p class="c_2fr39 b_tfnz2">Подробнее</p>
</a>
<div class="b_otttb"><a class="c_2fr39 b_tfnz2" href="/form/exhibition/{{i._id}}">Редактировать</a><span onclick="deleteExhibition(this, '{{i._id}}')" class="c_2fr39 b_tfnz2">Удалить</span></div>
<p class="c_drpqj b_mp1wb">{{ ( i._id.generation_time.strftime('%d.%m.%Y')) if i._id else ''}}</p>
</div>
</article>
{% for doc in data if doc.status in statuses %}
{{ render_doc(doc) }}
{% endfor %}
{% for doc in data if doc.status not in statuses %}
{{ render_doc(doc) }}
{% endfor %}
<script defer>
const query = window.location.href.split('?')[1] || '';
let exhibition_id = null;
if (query) {
const parts = query.split('=');
if (parts[0] === 'exhibition_id' && parts[1] !== 'None') {
exhibition_id = parts[1];
}
}
function filterFigure(exhibition_id) {
document.getElementById('main').querySelectorAll('figure').forEach(figure => {
if (!exhibition_id || figure.dataset.exhibition_id === exhibition_id) {
figure.style.display = 'flex';
console.log(figure.dataset.exhibition_id, exhibition_id, 'flex')
} else {
console.log(figure.dataset.exhibition_id, exhibition_id)
figure.style.display = 'none';
}
});
}
if (exhibition_id) {
document.getElementById('exhibition_id').value = exhibition_id;
}
filterFigure(exhibition_id);
</script>
<script>
const deliveryType = document.getElementById('delivery_type');
const track = document.getElementById('track');
function toggleTrack() {
const isSelf = deliveryType.value === 'Самостоятельно';
if (isSelf) {
track.required = false;
track.value = '';
} else {
track.required = true;
}
}
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>
@ -329,6 +155,7 @@ async function btnSend(btn, itemId, sender, too){
{{ header.style() }}
{{ competition.style() }}
{{ profile_nav.style() }}
{{card.style()}}
<style>/* Base (common) styles */
.c_aihm6{width: 100%;}
.mw{margin: 0 20px;