avtopost.kultmost.ru/templates/profile/competition/index.html
2026-09-23 06:34:07 -04:00

455 lines
16 KiB
HTML
Raw 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" %}
{% import "/components/header.html" as header %}
{% import "/components/footer.html" as footer %}
{% import "profile/competition/select.html" as card %}
{% import "components/profile_card.html" as profile_card %}
{% import "components/profile_nav.html" as profile_nav %}
{% import "components/checkbox.html" as checkbox %}
{% block title %}Отправка заявки на конкурс{% endblock %}
{% 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, 'competitions') }}
<div class="c_3ihvi" id="main">
<h2>{{competition.title}}</h2>
<p>{{competition.topic}}</p>
<p>Минимальное кол-во картин: {{competition.min_paintings}}. Максимальное кол-во картин: {{competition.max_paintings}}</p>
<a class="c_ctfod b_289i1 ba" href="{% if competition.get('rules_link') %}{{competition.rules_link}}{% else %}/b/competition/{{ competition.rules_file_id }}{% endif %}" rel="noopener noreferrer"><span class="c_d2l0b b_al8e7">Условия конкурса</span></a>
<a class="c_ctfod b_289i1 ba" href="/form/post?next=/profile/competition/{{ competition._id }}"><span class="c_d2l0b b_al8e7">Загрузить картину в список</span></a>
</div>
<div class="c_3ihvi">
<h3>Выбрано картин: <span id="selected_span">0</span></h3>
</div>
<div class="c_3ihvi">
<label class="offer-label">
<input type="checkbox" id="acceptOffer" class="offer-checkbox">
<span class="offer-text">Принять условия конкурса</span>
</label>
{% if competition.user._id == '681684390000000000000000' %}
<a class="c_ctfod b_289i1 ba" href="https://vk.ru/im?sel=25126640" rel="noopener noreferrer">
<span class="c_d2l0b b_al8e7">Задать вопрос в VK</span>
</a>
{% endif %}
<button class="c_ctfod b_289i1 ba" id="sendForm">
<span class="c_d2l0b b_al8e7">Отправить заявку</span>
</button>
</div>
<div class="c_3ihvi">
{% if posts|length > 5 %}
<div class="inptS">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M19.6 21L13.3 14.7C12.8 15.1 12.225 15.4167 11.575 15.65C10.925 15.8833 10.2333 16 9.5 16C7.68333 16 6.14583 15.3708 4.8875 14.1125C3.62917 12.8542 3 11.3167 3 9.5C3 7.68333 3.62917 6.14583 4.8875 4.8875C6.14583 3.62917 7.68333 3 9.5 3C11.3167 3 12.8542 3.62917 14.1125 4.8875C15.3708 6.14583 16 7.68333 16 9.5C16 10.2333 15.8833 10.925 15.65 11.575C15.4167 12.225 15.1 12.8 14.7 13.3L21 19.6L19.6 21ZM9.5 14C10.75 14 11.8125 13.5625 12.6875 12.6875C13.5625 11.8125 14 10.75 14 9.5C14 8.25 13.5625 7.1875 12.6875 6.3125C11.8125 5.4375 10.75 5 9.5 5C8.25 5 7.1875 5.4375 6.3125 6.3125C5.4375 7.1875 5 8.25 5 9.5C5 10.75 5.4375 11.8125 6.3125 12.6875C7.1875 13.5625 8.25 14 9.5 14Z"
fill="#1F1F1F"></path>
</svg>
<input id="memberSearch" list="membersList" type="search" placeholder="Поиск ..." aria-label="Поиск" />
<datalist id="membersList">
{% for post in posts %}
<option value="{{ post.title or '' }}"></option>
{% endfor %}
</datalist>
</div>
{% endif %}
</div>
<div class="c_tihtk">
{% for post in posts %}
{{ card.section(post, competition.get('nominations',[])) }}
{% endfor %}
</div>
<script>
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
(function(){
const input = document.getElementById('memberSearch');
const container = document.querySelector('.c_tihtk');
if (!input || !container) return;
const itemsSelector = '.c_tihtk > figure';
const nameSelector = '.c_n5g0e';
const norm = s => (s || '').toString().trim().toLowerCase();
function filter() {
const q = norm(input.value);
const items = container.querySelectorAll(itemsSelector);
items.forEach(a => {
const nameEl = a.querySelector(nameSelector);
const name = norm(nameEl ? nameEl.textContent : '');
a.style.display = (q === '' || name.indexOf(q) !== -1) ? '' : 'none';
});
}
input.addEventListener('input', filter);
input.addEventListener('change', filter);
input.addEventListener('keydown', (e) => { if (e.key === 'Escape') { input.value = ''; filter(); } });
// initial: show all
filter();
})();
// @license-end
</script>
<script>
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
document.querySelectorAll('.post').forEach(postEl => {
const fg = postEl.querySelector('.c_nb6gy.floating-group');
if (!fg) return;
// ensure paragraph exists immediately before the floating-group
let prev = fg.previousElementSibling;
let p;
if (prev && prev.classList && prev.classList.contains('post-description')) {
p = prev;
} else {
p = document.createElement('p');
p.className = 'post-description c_t5wnp b_mjekb';
p.textContent = ''; // initially empty
fg.parentNode.insertBefore(p, fg);
}
const textarea = fg.querySelector('textarea[name="description"]');
const button = postEl.querySelector('.saves[data-post_id]');
if (!button) return;
button.addEventListener('click', async () => {
const postId = button.getAttribute('data-post_id');
const text = textarea ? textarea.value.trim() : '';
try {
p.textContent = text;
fg.style.display = 'none';
button.style.display = 'none';
const res = await fetch('/api/post/description', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ post_id: postId, description: text })
});
if (!res.ok) throw new Error('Network response was not ok');
} catch (err) {
console.error(err);
}
});
});
// @license-end
</script>
<script>
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
const min_paintings = parseInt('{{competition.min_paintings}}')
const max_paintings = parseInt('{{competition.max_paintings}}')
const competition_id = '{{competition._id}}'
let selectedPosts = [];
function selectNom(el) {
console.log(el)
el.parentElement.classList.remove('error');
}
function saveCompetitionIds(postId) {
const key = 'competition_ids'+competition_id;
const stored = localStorage.getItem(key);
const ids = stored ? JSON.parse(stored) : [];
if (!ids.includes(postId)) {
ids.push(postId);
localStorage.setItem(key, JSON.stringify(ids));
}
}
function removeCompetitionId(postId) {
const key = 'competition_ids' + competition_id;
const stored = localStorage.getItem(key);
// If nothing is stored yet, there’s nothing to remove
if (!stored) return;
const ids = JSON.parse(stored);
const index = ids.indexOf(postId);
// Only act if the ID is actually present
if (index !== -1) {
ids.splice(index, 1); // delete the element
localStorage.setItem(key, JSON.stringify(ids));
}
}
function loadAndClick() {
const key = 'competition_ids'+competition_id;
const stored = localStorage.getItem(key);
if (!stored) return;
const ids = JSON.parse(stored);
const container = document.querySelector('.c_tihtk');
ids.forEach(postId => {
const figure = container.querySelector(`figure[data-post-id="${postId}"]`);
if (!figure) return;
const button = figure.querySelector('button.c_kg401');
if (button) button.click();
});
}
const container = document.querySelector('.c_tihtk');
const sendFormBtn = document.getElementById('sendForm');
function checkAllDescriptions() {
for (let postId of selectedPosts) {
const figure = container.querySelector(`figure[data-post-id="${postId}"]`);
if (!figure) continue;
const select = figure.querySelector('select');
const textarea = figure.querySelector('textarea');
if (select && !select.value.trim()) {
const floatingGroup = select.parentElement;
if (floatingGroup) floatingGroup.classList.add('error');
select.focus();
showToast({message: 'Пожалуйста, выберите номинацию!', status: 'error'});
return false;
}
if (textarea && !textarea.value.trim()) {
// Подсвечиваем ошибку
const floatingGroup = figure.querySelector('.floating-group');
if (floatingGroup) floatingGroup.classList.add('error');
textarea.focus();
showToast({message: 'Пожалуйста, заполните описание для всех выбранных постов!', status: 'error'});
return false;
}
}
return true;
}
container.addEventListener('click', (e) => {
const btn = e.target.closest('button.c_kg401');
if (!btn) return;
const figure = btn.closest('figure.c_45o96');
if (!figure) return;
const postId = figure.dataset.postId;
if (!postId) return;
const textarea = figure.querySelector('textarea');
const select = figure.querySelector('select');
const floatingGroup = figure.querySelector('.floating-group');
// === Если карточка уже выбрана — отменяем выбор ===
if (selectedPosts.includes(postId)) {
// Снимаем выбор
selectedPosts = selectedPosts.filter(id => id !== postId);
selected_span.textContent = selectedPosts.length
removeCompetitionId(postId)
// Возвращаем кнопку в исходное состояние
btn.style.backgroundColor = '';
btn.style.border = '1px solid #5F1798';
btn.querySelector('svg').querySelectorAll('rect')[0].style.fill='#5F1798'
btn.querySelector('svg').querySelectorAll('rect')[1].style.stroke='#5F1798'
const span = btn.querySelector('span.c_shz86');
if (span) span.textContent = 'Выбрать';
if (textarea) {textarea.parentNode.classList.remove('error');}
if (select) {select.parentNode.classList.remove('error');}
return;
}
// === Новая выборка ===
if (textarea) {
if (!textarea.value.trim()) {
floatingGroup.classList.add('error');
textarea.focus();
} else {
floatingGroup.classList.remove('error');
}
}
if (select) {
if (!select.value.trim()) {
select.parentNode.classList.add('error');
// select.focus();
} else {
select.parentNode.classList.remove('error');
}
}
if (selectedPosts.length > max_paintings) {
showToast({message: `Максимальное кол-во картин: ${max_paintings}`, status: 'error'});
return;
}
selectedPosts.push(postId);
selected_span.textContent = selectedPosts.length
// Меняем кнопку
btn.style.backgroundColor = 'rgba(14, 214, 70, 0.10)';
btn.style.border = '1px solid #0ED646';
btn.querySelector('svg').querySelectorAll('rect')[0].style.fill='#0ED646'
btn.querySelector('svg').querySelectorAll('rect')[1].style.stroke='#0ED646'
saveCompetitionIds(postId);
const span = btn.querySelector('span.c_shz86');
if (span) span.textContent = 'Выбрано';
});
// === Отправка формы ===
if (sendFormBtn) {
sendFormBtn.addEventListener('click', () => {
if (selectedPosts.length < min_paintings) {
showToast({message: `Минимальное кол-во картин: ${min_paintings}`, status: 'error'});
return;
}
const offerAccepted = document.getElementById('acceptOffer')?.checked;
if (!offerAccepted) {
showToast({message: 'Необходимо принять оферту', status: 'error'});
return;
}
if (!checkAllDescriptions()) {
return;
}
const postsToSend = selectedPosts.map(postId => {
const nominationEl = document.querySelector(`#nom_${postId}`);
const nom_name = nominationEl?.value?.trim() || null;
const textarea = container.querySelector(`figure[data-post-id="${postId}"] textarea`);
const doc = {
postId: postId,
description: textarea ? textarea.value.trim() : null,
};
if (nom_name) doc.nomination = nom_name;
return doc;
});
// Отправляем на сервер
fetch('/api/send_competition_request/'+competition_id, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
posts: postsToSend
})
})
.then(res => {
if (res.ok) {
window.location.href='/competition/{{competition._id}}#{{user._id}}'
sendFormBtn.disabled = true;
} else {
showToast({message: 'Ошибка при отправке заявки', status: 'error'});
}
})
.catch(() => {
showToast({message: 'Ошибка соединения с сервером', status: 'error'});
});
});
}
// Дополнительно: обновляем состояние кнопки при вводе текста в любое textarea
container.addEventListener('input', (e) => {
if (e.target.tagName === 'TEXTAREA') {
const floatingGroup = e.target.closest('.floating-group');
if (floatingGroup) {
if (e.target.value.trim()) {
floatingGroup.classList.remove('error');
}
}
updateSendButtonState();
}
});
// @license-end
</script>
</div>
</div>
</section>
<script>
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
loadAndClick();
document.addEventListener('DOMContentLoaded', () => {
const avatarInput = document.getElementById('avatar');
const preview = document.getElementById('preview');
avatarInput.addEventListener('change', async () => {
if (avatarInput.files.length === 0) return;
const file = avatarInput.files[0];
preview.src = URL.createObjectURL(file);
const form = new FormData();
form.append('avatar', file);
try {
const resp = await fetch('/api/avatar', {
method: 'POST',
body: form
});
if (!resp.ok) {
const text = await resp.text();
throw new Error(`Upload failed: ${resp.status} ${text}`);
}
const data = await resp.json();
if (data.url) preview.src = data.url;
console.log('Upload success', data);
} catch (err) {
console.error(err);
} finally {
URL.revokeObjectURL(preview.src);
}
});
});
// @license-end
</script>
{{ footer.section(user) }}
{% endblock %}
{% block head %}
{{ header.style() }}
{{ card.style() }}
{{ profile_nav.style() }}
{{ checkbox.style() }}
<style>
.inptS{height: 48px;
display: flex; width: 335px;
align-items: center;gap: 10px; border-bottom: 1px solid #5F1798;}
/* Mobile */
@media (max-width: 699px) {
.c_ctfod{width: 100%;
box-sizing: border-box;}
.c_mrl59 { align-items: center; background: #5F1798; border-radius: 25px; bottom: -15px; display: flex; height: 50px; justify-content: center; position: absolute; right: -15px; width: 50px; }
}
/* Tablet */
@media (min-width: 700px) and (max-width: 900px) {
.c_ctfod{width: 335px;
box-sizing: border-box;}.c_nnsh6>a>img{display: none;}
.c_iiehk { align-items: center; border-radius: 5px; display: flex; flex-direction: column; gap: 20px; width: 335px; }
}
/* Desktop */
@media (min-width: 901px) {
.c_ctfod{width: 335px;
box-sizing: border-box;}.c_nnsh6>a>img{display: none;}
.c_iiehk { align-items: flex-end; border-radius: 5px; display: flex; flex-direction: column; gap: 20px; width: 310px; }
}</style>
{{ footer.style() }}
{% endblock %}