Item list management
This commit is contained in:
parent
471e465133
commit
ab123ba7b1
@ -2,8 +2,6 @@
|
|||||||
{% load polyphonic %}
|
{% load polyphonic %}
|
||||||
|
|
||||||
{% block page %}
|
{% block page %}
|
||||||
<form action="" method="post" target="_blank">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div>
|
<div>
|
||||||
<div class="field is-grouped is-grouped-centered is-grouped-multiline">
|
<div class="field is-grouped is-grouped-centered is-grouped-multiline">
|
||||||
<div class="field has-addons control has-addons-centered is-expanded">
|
<div class="field has-addons control has-addons-centered is-expanded">
|
||||||
@ -16,7 +14,7 @@
|
|||||||
<input type="text" class="input" list="instrument-list" id="instrument-name" onchange="updateParts()"/>
|
<input type="text" class="input" list="instrument-list" id="instrument-name" onchange="updateParts()"/>
|
||||||
<datalist id="instrument-list">
|
<datalist id="instrument-list">
|
||||||
{% for inst in instruments %}
|
{% for inst in instruments %}
|
||||||
<option value="{{inst.1}}"/>
|
<option value="{{inst}}"/>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</datalist>
|
</datalist>
|
||||||
</span>
|
</span>
|
||||||
@ -35,6 +33,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form action="" method="post" target="_blank">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
<table class="table is-striped mx-auto">
|
<table class="table is-striped mx-auto">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -58,7 +59,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="is-hidden-mobile">{{ item.work.composer }}</td>
|
<td class="is-hidden-mobile">{{ item.work.composer }}</td>
|
||||||
<td class="is-hidden-mobile">{% firstof item.work.running_time "------" %}</td>
|
<td class="is-hidden-mobile">{% firstof item.work.running_time "-:--" %}</td>
|
||||||
<td class="select-cell">
|
<td class="select-cell">
|
||||||
<input type="hidden" name="works" value="{{ item.work.pk }}"/>
|
<input type="hidden" name="works" value="{{ item.work.pk }}"/>
|
||||||
<span class="select is-small" style="width: 100%">
|
<span class="select is-small" style="width: 100%">
|
||||||
@ -84,7 +85,7 @@
|
|||||||
<td/>
|
<td/>
|
||||||
<td/>
|
<td/>
|
||||||
<td/>
|
<td/>
|
||||||
<td>{% firstof running_time "------" %}</td>
|
<td>{% firstof running_time "-:--" %}</td>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<button class="button is-link is-small" type="submit" name="action" value="pdf">
|
<button class="button is-link is-small" type="submit" name="action" value="pdf">
|
||||||
{% icon "two_pager" %}
|
{% icon "two_pager" %}
|
||||||
@ -114,50 +115,49 @@
|
|||||||
const INSTRUMENTS = JSON.parse(document.getElementById('instruments').innerText);
|
const INSTRUMENTS = JSON.parse(document.getElementById('instruments').innerText);
|
||||||
|
|
||||||
function updateParts() {
|
function updateParts() {
|
||||||
var inst = document.getElementById("instrument-name").value.toLowerCase();
|
var inst = document.getElementById("instrument-name").value;
|
||||||
window.localStorage.setItem('instrument-name', inst);
|
window.localStorage.setItem('instrument-name', inst);
|
||||||
console.log("Changing to", inst);
|
console.log("Changing to", inst);
|
||||||
|
|
||||||
for (let i of INSTRUMENTS) {
|
for (let i in INSTRUMENTS) {
|
||||||
if (i[1].toLowerCase() === inst) {
|
if (i.toLowerCase() === inst.toLowerCase()) {
|
||||||
inst = i[0];
|
inst = i;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("Instrument code:", inst);
|
|
||||||
|
|
||||||
|
|
||||||
let part = document.getElementById("part-preference").value;
|
let part = document.getElementById("part-preference").value;
|
||||||
window.localStorage.setItem('part-preference', part);
|
window.localStorage.setItem('part-preference', part);
|
||||||
console.log("Part preference:", part);
|
console.log("Part preference:", inst, part);
|
||||||
|
|
||||||
|
|
||||||
selectParts(inst, part);
|
selectParts(INSTRUMENTS[inst] || [inst.toLowerCase()], part);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectParts(inst, part) {
|
function selectParts(codes, part) {
|
||||||
|
|
||||||
|
let n = parseInt(part);
|
||||||
|
|
||||||
|
let preferences = [];
|
||||||
|
for (let i=n; i>0; i--) {
|
||||||
|
for (let code of codes) preferences.push(code + "-" + i);
|
||||||
|
}
|
||||||
|
preferences.push(...codes);
|
||||||
|
console.log("Preferences:", preferences);
|
||||||
|
|
||||||
let prefix = inst + "-" + part;
|
|
||||||
|
|
||||||
let selections = document.getElementsByName("instrument-selection");
|
let selections = document.getElementsByName("instrument-selection");
|
||||||
console.log("Updating selections:", prefix, selections);
|
|
||||||
for(let i=0; i<selections.length; i++) {
|
for(let i=0; i<selections.length; i++) {
|
||||||
var result = "-"
|
const selection = selections[i];
|
||||||
let options = selections[i].children;
|
for(let pref of preferences) {
|
||||||
for(let j=0; j<options.length; j++) {
|
selection.value = pref;
|
||||||
let value = options[j].value;
|
if(selection.value == pref) break;
|
||||||
if (value.startsWith(prefix)) {
|
|
||||||
result = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (result==="-" && value.startsWith(inst)) {
|
|
||||||
result = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log("Selected:", result);
|
|
||||||
selections[i].value = result;
|
if(!selection.value) selection.value = "-";
|
||||||
|
|
||||||
|
console.log("Selected:", selection.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ function downloadPart(collection, work) {
|
|||||||
}
|
}
|
||||||
console.log(part);
|
console.log(part);
|
||||||
let url = "/collections/" + collection + "/works/" + work + "/download?tag=" + part;
|
let url = "/collections/" + collection + "/works/" + work + "/download?tag=" + part;
|
||||||
window.location = url;
|
window.open(url, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("instrument-name").value = localStorage.getItem('instrument-name', 'All');
|
document.getElementById("instrument-name").value = localStorage.getItem('instrument-name', 'All');
|
||||||
|
|||||||
@ -13,37 +13,57 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block page %}
|
{% block page %}
|
||||||
<table style="max-width: 600px; margin: 10pt auto;" class="zebra">
|
<div class="columns">
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Item</th>
|
|
||||||
<th>Time</th>
|
|
||||||
<th/>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="work-list">
|
|
||||||
{% for item in object_list %}
|
|
||||||
<tr data-pk="{{ item.pk }}" data-order="{{ forloop.counter }}">
|
|
||||||
<td>{{ item.work.name }}</td>
|
|
||||||
<td>{{ item.work.duration }}</td>
|
|
||||||
<td style="text-align: center;">
|
|
||||||
<span class="clickable" title="Move up" onclick="moveItem({{ item.pk }}, -1)">
|
|
||||||
{% icon "arrow_upward" %}
|
|
||||||
</span>
|
|
||||||
<span class="clickable" title="Move down" onclick="moveItem({{ item.pk }}, 1)">
|
|
||||||
{% icon "arrow_downward" %}
|
|
||||||
</span>
|
|
||||||
<span class="clickable" title="Remove" onClick="moveItem({{ item.pk }}, 0)">
|
|
||||||
{% icon "delete" %}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
<div class="column">
|
||||||
|
<h3 class="subtitle">Add from collection</h3>
|
||||||
|
<div>
|
||||||
|
{% for access in project.ensemble.allowed_collections.all %}
|
||||||
|
<a href="" class="button is-link">{{ access.collection.name }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column card">
|
||||||
|
<table style="max-width: 600px; margin: 10pt auto;" class="table is-striped is-narrow">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Item</th>
|
||||||
|
<th>Time</th>
|
||||||
|
<th/>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="work-list">
|
||||||
|
{% for item in object_list %}
|
||||||
|
<tr data-pk="{{ item.pk }}" data-order="{{ forloop.counter }}">
|
||||||
|
<td>{{ item.work.name }}</td>
|
||||||
|
<td style="min-width: 100px;">{% firstof item.work.duration '-:--' %}</td>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<span class="clickable cursor-pointer" title="Remove" onClick="removeItem({{ item.pk }})">
|
||||||
|
{% icon "delete" %}
|
||||||
|
</span>
|
||||||
|
<span class="clickable drag-handle cursor-drag" title="Reorder">
|
||||||
|
{% icon "reorder" %}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr><td colspan="2">No items</td></tr>
|
<tr><td colspan="2">No items</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<style>
|
||||||
|
tr.dragging { opacity: 0.4; }
|
||||||
|
.drag-handle { cursor: move; }
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
@ -51,36 +71,81 @@
|
|||||||
|
|
||||||
let workList = document.getElementById('work-list');
|
let workList = document.getElementById('work-list');
|
||||||
var dirty = false;
|
var dirty = false;
|
||||||
|
var dragSrc = null;
|
||||||
|
var dragSrcNext = null;
|
||||||
|
|
||||||
function moveItem(pk, dir) {
|
function reorderItems() {
|
||||||
|
let items = Array.prototype.slice.call(workList.children, 0);
|
||||||
|
for (let j = 0; j < items.length; j++) {
|
||||||
|
items[j].dataset.order = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeItem(pk) {
|
||||||
let items = Array.prototype.slice.call(workList.children, 0);
|
let items = Array.prototype.slice.call(workList.children, 0);
|
||||||
var i=0;
|
|
||||||
pk = "" + pk;
|
pk = "" + pk;
|
||||||
for(i=0; i<items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
if(items[i].dataset.pk === pk) break;
|
if (items[i].dataset.pk === pk) {
|
||||||
}
|
items[i].dataset.order = -1;
|
||||||
if(i >= items.length) return;
|
items[i].style = "display: none";
|
||||||
|
break;
|
||||||
// check the direction is sensible
|
}
|
||||||
if (i + dir < 0 || i + dir >= items.length) return;
|
|
||||||
|
|
||||||
if (dir === 0) {
|
|
||||||
items[i].dataset.order = -1;
|
|
||||||
items[i].style = "display: none";
|
|
||||||
} else {
|
|
||||||
items[i].dataset.order = parseInt(items[i].dataset.order) + dir;
|
|
||||||
items[i+dir].dataset.order = parseInt(items[i+dir].dataset.order) - dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.sort((a, b) => parseInt(a.dataset.order) - parseInt(b.dataset.order))
|
|
||||||
|
|
||||||
workList.innerHTML = ""
|
|
||||||
for(let j=0; j<items.length; j++) {
|
|
||||||
workList.appendChild(items[j]);
|
|
||||||
}
|
}
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workList.addEventListener('mousedown', function(e) {
|
||||||
|
let handle = e.target.closest('.drag-handle');
|
||||||
|
if (handle) handle.closest('tr').draggable = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
workList.addEventListener('mouseup', function(e) {
|
||||||
|
let handle = e.target.closest('.drag-handle');
|
||||||
|
if (handle) handle.closest('tr').draggable = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
workList.addEventListener('dragstart', function(e) {
|
||||||
|
dragSrc = e.target.closest('tr');
|
||||||
|
if (!dragSrc) return;
|
||||||
|
dragSrcNext = dragSrc.nextElementSibling;
|
||||||
|
dragSrc.classList.add('dragging');
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData('text/plain', dragSrc.dataset.pk);
|
||||||
|
});
|
||||||
|
|
||||||
|
workList.addEventListener('dragover', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = 'move';
|
||||||
|
let target = e.target.closest('tr');
|
||||||
|
if (!target || target === dragSrc) return;
|
||||||
|
|
||||||
|
let rect = target.getBoundingClientRect();
|
||||||
|
let midY = rect.top + rect.height / 2;
|
||||||
|
|
||||||
|
if (e.clientY < midY) {
|
||||||
|
workList.insertBefore(dragSrc, target);
|
||||||
|
} else {
|
||||||
|
workList.insertBefore(dragSrc, target.nextElementSibling);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
workList.addEventListener('drop', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dragSrcNext = null;
|
||||||
|
reorderItems();
|
||||||
|
dirty = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
workList.addEventListener('dragend', function(e) {
|
||||||
|
if (!dragSrc) return;
|
||||||
|
dragSrc.classList.remove('dragging');
|
||||||
|
if (dragSrcNext) {
|
||||||
|
workList.insertBefore(dragSrc, dragSrcNext);
|
||||||
|
}
|
||||||
|
dragSrc = null;
|
||||||
|
dragSrcNext = null;
|
||||||
|
});
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
|
|
||||||
let items = Array.prototype.slice.call(workList.children, 0);
|
let items = Array.prototype.slice.call(workList.children, 0);
|
||||||
@ -98,7 +163,7 @@ function save() {
|
|||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
dirty=false;
|
dirty=false;
|
||||||
window.location = "{% url 'item_list' project=project.pk %}";
|
window.location = "{% url 'project_detail' project=project.pk %}";
|
||||||
} else {
|
} else {
|
||||||
alert("Failed: " + response.statusText)
|
alert("Failed: " + response.statusText)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,11 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
path("library", views.LibraryWorkListView.as_view(), name="work_list"),
|
path("library", views.LibraryWorkListView.as_view(), name="work_list"),
|
||||||
path("collections", views.CollectionListView.as_view(), name="collection_list"),
|
path("collections", views.CollectionListView.as_view(), name="collection_list"),
|
||||||
|
path(
|
||||||
|
"projects/<int:project>/collections",
|
||||||
|
views.ProjectWorkListView.as_view(),
|
||||||
|
name="project_work_list",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"collections/<int:collection>",
|
"collections/<int:collection>",
|
||||||
views.CollectionWorkListView.as_view(),
|
views.CollectionWorkListView.as_view(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user