| 240 |
240 |
|
</div>
|
| 241 |
241 |
|
{% endif %}
|
| 242 |
242 |
|
{% endif %}
|
|
243 |
+ |
|
|
244 |
+ |
{% if session_user.is_some() %}
|
|
245 |
+ |
<div style="margin-top: 1rem; position: relative;">
|
|
246 |
+ |
<button class="secondary" style="width: 100%; font-size: 0.9rem;"
|
|
247 |
+ |
onclick="toggleCollectionDropdown('{{ item.id }}')">Save to collection</button>
|
|
248 |
+ |
<div id="collection-dropdown"
|
|
249 |
+ |
style="display: none; position: absolute; left: 0; right: 0; top: 100%; z-index: 10;
|
|
250 |
+ |
background: var(--background); border: 1px solid var(--border);
|
|
251 |
+ |
box-shadow: 0 2px 8px rgba(0,0,0,0.1); max-height: 240px; overflow-y: auto;">
|
|
252 |
+ |
<div id="collection-list" style="padding: 0.5rem;"></div>
|
|
253 |
+ |
<div style="border-top: 1px solid var(--border); padding: 0.5rem;">
|
|
254 |
+ |
<form onsubmit="createAndAddCollection('{{ item.id }}', this); return false;"
|
|
255 |
+ |
style="display: flex; gap: 0.5rem;">
|
|
256 |
+ |
<input type="text" name="title" placeholder="New collection" required
|
|
257 |
+ |
style="flex: 1; padding: 0.3rem 0.5rem; font-size: 0.85rem;">
|
|
258 |
+ |
<button class="secondary" type="submit" style="font-size: 0.85rem; white-space: nowrap;">Create</button>
|
|
259 |
+ |
</form>
|
|
260 |
+ |
</div>
|
|
261 |
+ |
</div>
|
|
262 |
+ |
</div>
|
|
263 |
+ |
{% endif %}
|
| 243 |
264 |
|
</div>
|
| 244 |
265 |
|
</div>
|
| 245 |
266 |
|
|
| 457 |
478 |
|
}
|
| 458 |
479 |
|
})();
|
| 459 |
480 |
|
</script>
|
|
481 |
+ |
|
|
482 |
+ |
<script>
|
|
483 |
+ |
(function() {
|
|
484 |
+ |
var dropdownOpen = false;
|
|
485 |
+ |
|
|
486 |
+ |
window.toggleCollectionDropdown = function(itemId) {
|
|
487 |
+ |
var dd = document.getElementById('collection-dropdown');
|
|
488 |
+ |
if (!dd) return;
|
|
489 |
+ |
dropdownOpen = !dropdownOpen;
|
|
490 |
+ |
dd.style.display = dropdownOpen ? 'block' : 'none';
|
|
491 |
+ |
if (dropdownOpen) loadCollections(itemId);
|
|
492 |
+ |
};
|
|
493 |
+ |
|
|
494 |
+ |
function loadCollections(itemId) {
|
|
495 |
+ |
var list = document.getElementById('collection-list');
|
|
496 |
+ |
list.innerHTML = '<div style="padding: 0.5rem; opacity: 0.6; font-size: 0.85rem;">Loading...</div>';
|
|
497 |
+ |
fetch('/api/collections/for-item/' + itemId, { headers: csrfHeaders() })
|
|
498 |
+ |
.then(function(r) { return r.json(); })
|
|
499 |
+ |
.then(function(collections) {
|
|
500 |
+ |
if (collections.length === 0) {
|
|
501 |
+ |
list.innerHTML = '<div style="padding: 0.5rem; opacity: 0.6; font-size: 0.85rem;">No collections yet. Create one below.</div>';
|
|
502 |
+ |
return;
|
|
503 |
+ |
}
|
|
504 |
+ |
var html = '';
|
|
505 |
+ |
for (var i = 0; i < collections.length; i++) {
|
|
506 |
+ |
var c = collections[i];
|
|
507 |
+ |
html += '<label style="display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0; cursor: pointer; font-size: 0.9rem;">'
|
|
508 |
+ |
+ '<input type="checkbox"' + (c.in_collection ? ' checked' : '')
|
|
509 |
+ |
+ ' onchange="toggleCollectionItem(\'' + c.id + '\', \'' + itemId + '\', this.checked)">'
|
|
510 |
+ |
+ escapeHtml(c.title) + '</label>';
|
|
511 |
+ |
}
|
|
512 |
+ |
list.innerHTML = html;
|
|
513 |
+ |
})
|
|
514 |
+ |
.catch(function() {
|
|
515 |
+ |
list.innerHTML = '<div style="padding: 0.5rem; color: var(--danger); font-size: 0.85rem;">Failed to load collections.</div>';
|
|
516 |
+ |
});
|
|
517 |
+ |
}
|
|
518 |
+ |
|
|
519 |
+ |
window.toggleCollectionItem = function(collectionId, itemId, add) {
|
|
520 |
+ |
fetch('/api/collections/' + collectionId + '/items/' + itemId, {
|
|
521 |
+ |
method: add ? 'POST' : 'DELETE',
|
|
522 |
+ |
headers: csrfHeaders()
|
|
523 |
+ |
});
|
|
524 |
+ |
};
|
|
525 |
+ |
|
|
526 |
+ |
window.createAndAddCollection = function(itemId, form) {
|
|
527 |
+ |
var title = form.title.value.trim();
|
|
528 |
+ |
if (!title) return;
|
|
529 |
+ |
var slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
530 |
+ |
fetch('/api/collections', {
|
|
531 |
+ |
method: 'POST',
|
|
532 |
+ |
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
533 |
+ |
body: JSON.stringify({ title: title, slug: slug, description: '', is_public: false })
|
|
534 |
+ |
})
|
|
535 |
+ |
.then(function(r) { return r.json(); })
|
|
536 |
+ |
.then(function(col) {
|
|
537 |
+ |
return fetch('/api/collections/' + col.id + '/items/' + itemId, {
|
|
538 |
+ |
method: 'POST',
|
|
539 |
+ |
headers: csrfHeaders()
|
|
540 |
+ |
});
|
|
541 |
+ |
})
|
|
542 |
+ |
.then(function() {
|
|
543 |
+ |
form.title.value = '';
|
|
544 |
+ |
loadCollections(itemId);
|
|
545 |
+ |
});
|
|
546 |
+ |
};
|
|
547 |
+ |
|
|
548 |
+ |
function escapeHtml(s) {
|
|
549 |
+ |
var d = document.createElement('div');
|
|
550 |
+ |
d.textContent = s;
|
|
551 |
+ |
return d.innerHTML;
|
|
552 |
+ |
}
|
|
553 |
+ |
|
|
554 |
+ |
document.addEventListener('click', function(e) {
|
|
555 |
+ |
var dd = document.getElementById('collection-dropdown');
|
|
556 |
+ |
if (dd && dropdownOpen && !dd.parentElement.contains(e.target)) {
|
|
557 |
+ |
dd.style.display = 'none';
|
|
558 |
+ |
dropdownOpen = false;
|
|
559 |
+ |
}
|
|
560 |
+ |
});
|
|
561 |
+ |
})();
|
|
562 |
+ |
</script>
|
| 460 |
563 |
|
{% endblock %}
|