feat: implement per-order toggle-all material status and real-time usage statistics UI
This commit is contained in:
parent
26c07a769f
commit
fd10b3f8f7
4 changed files with 105 additions and 23 deletions
|
|
@ -68,14 +68,9 @@
|
|||
<div class="d-flex align-items-center gap-2">
|
||||
{% with items=order.preparationorderitem_set.all other_items=order.other_items.all %}
|
||||
{% if items or other_items %}
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary py-1 px-2"
|
||||
style="font-size:0.75rem;"
|
||||
onclick="toggleAllMaterialUsed('{{ intervention.id }}', '{{ order.id }}')"
|
||||
{% if not can_edit_intervention %}disabled{% endif %}
|
||||
title="{% translate 'Cocher / décocher tout pour ce bon' %}">
|
||||
<i class="bi bi-check2-all"></i> {% translate "Tout cocher (ce bon)" %}
|
||||
</button>
|
||||
<span class="badge bg-white text-dark border small" id="order-stats-badge-{{ order.id }}">
|
||||
<span id="order-used-count-{{ order.id }}">{{ order.used_items_count }}</span> / {{ order.total_items_count }} {% translate "utilisé(s)" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<a href="{% url 'stock:preparation_order_detail' order.pk %}"
|
||||
|
|
@ -92,7 +87,20 @@
|
|||
<table class="table table-sm table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width: 100px;" class="text-center">{% translate "Utilisé ?" %}</th>
|
||||
<th style="width: 130px;" class="text-center align-middle py-2">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-success d-inline-flex align-items-center gap-1 py-1 px-2 btn-order-toggle-all"
|
||||
id="btn-order-toggle-all-{{ order.id }}"
|
||||
onclick="toggleAllMaterialUsed('{{ intervention.id }}', '{{ order.id }}')"
|
||||
{% if not can_edit_intervention %}disabled title="{% translate 'Permissions insuffisantes' %}"{% else %}title="{% translate 'Tout cocher ou tout décocher pour ce bon' %}"{% endif %}
|
||||
style="font-size: 0.75rem;">
|
||||
<i class="bi bi-check-all fs-6"></i>
|
||||
<span class="btn-order-toggle-all-text" id="btn-order-toggle-all-text-{{ order.id }}">
|
||||
{% if order.all_items_used %}{% translate "Tout décocher" %}{% else %}{% translate "Tout cocher" %}{% endif %}
|
||||
</span>
|
||||
</button>
|
||||
<div class="text-muted" style="font-size: 0.7rem; font-weight: normal; margin-top: 2px;">{% translate "Utilisé ?" %}</div>
|
||||
</th>
|
||||
<th>{% translate "Article" %}</th>
|
||||
<th style="width: 150px;">{% translate "Référence" %}</th>
|
||||
<th style="width: 120px;" class="text-center">{% translate "Quantité" %}</th>
|
||||
|
|
|
|||
|
|
@ -612,6 +612,18 @@ function updateMaterialUI(data) {
|
|||
el.textContent = data.all_used ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
|
||||
if (data.order_id) {
|
||||
const orderAllUsed = data.order_all_used !== undefined ? data.order_all_used : data.all_used;
|
||||
const orderBtnTexts = document.querySelectorAll(`#btn-order-toggle-all-text-${data.order_id}, #btn-mobile-order-toggle-all-text-${data.order_id}`);
|
||||
orderBtnTexts.forEach(el => {
|
||||
el.textContent = orderAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
if (data.order_used_count !== undefined) {
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${data.order_id}, #mobile-order-used-count-${data.order_id}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = data.order_used_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.item_id !== undefined && data.is_other !== undefined) {
|
||||
const prefix = data.is_other ? 'other-' : 'item-';
|
||||
const badges = document.querySelectorAll(`#material-badge-${prefix}${data.item_id}, #mobile-material-item-badge-${prefix}${data.item_id}`);
|
||||
|
|
@ -670,7 +682,7 @@ function toggleAllMaterialUsed(interventionId, orderId) {
|
|||
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value
|
||||
|| document.cookie.split('; ').find(r => r.startsWith('csrftoken='))?.split('=')[1] || '';
|
||||
|
||||
const buttons = document.querySelectorAll('#btn-desktop-toggle-all-material, #btn-mobile-toggle-all-material');
|
||||
const buttons = document.querySelectorAll('#btn-desktop-toggle-all-material, #btn-mobile-toggle-all-material, .btn-order-toggle-all');
|
||||
buttons.forEach(b => b.disabled = true);
|
||||
|
||||
const formData = new FormData();
|
||||
|
|
@ -712,6 +724,27 @@ function toggleAllMaterialUsed(interventionId, orderId) {
|
|||
});
|
||||
});
|
||||
|
||||
if (orderId) {
|
||||
const orderBtnTexts = document.querySelectorAll(`#btn-order-toggle-all-text-${orderId}, #btn-mobile-order-toggle-all-text-${orderId}`);
|
||||
orderBtnTexts.forEach(el => {
|
||||
el.textContent = isAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
if (data.order_used_count !== undefined) {
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${orderId}, #mobile-order-used-count-${orderId}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = data.order_used_count);
|
||||
}
|
||||
} else {
|
||||
document.querySelectorAll('.btn-order-toggle-all-text').forEach(el => {
|
||||
el.textContent = isAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
document.querySelectorAll('.btn-order-toggle-all').forEach(btn => {
|
||||
const id = btn.id.replace('btn-order-toggle-all-', '').replace('btn-mobile-order-toggle-all-', '');
|
||||
const totalCheckboxes = document.querySelectorAll(`.material-used-checkbox[data-order-id="${id}"]`).length;
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${id}, #mobile-order-used-count-${id}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = isAllUsed ? totalCheckboxes : 0);
|
||||
});
|
||||
}
|
||||
|
||||
updateMaterialUI({
|
||||
used_count: data.used_count,
|
||||
total_count: data.total_count,
|
||||
|
|
|
|||
|
|
@ -1156,18 +1156,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
{% with items=order.preparationorderitem_set.all other_items=order.other_items.all %}
|
||||
{% if items or other_items %}
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary py-0 px-2"
|
||||
style="font-size:0.7rem;"
|
||||
onclick="toggleAllMaterialUsed('{{ intervention.id }}', '{{ order.id }}')"
|
||||
{% if not can_edit_intervention %}disabled{% endif %}
|
||||
title="{% translate 'Cocher / décocher tout pour ce bon' %}">
|
||||
<i class="bi bi-check2-all"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<a href="{% url 'mobile:stock_preparation_order_detail_mobile' order.pk %}"
|
||||
class="btn btn-sm btn-outline-primary p-1"
|
||||
title="{% translate 'Voir les détails' %}">
|
||||
|
|
@ -1179,6 +1167,22 @@
|
|||
{% with items=order.preparationorderitem_set.all other_items=order.other_items.all %}
|
||||
{% if items or other_items %}
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item px-3 py-2 bg-light border-bottom d-flex align-items-center justify-content-between">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-success d-inline-flex align-items-center gap-1 py-1 px-2 btn-order-toggle-all"
|
||||
id="btn-mobile-order-toggle-all-{{ order.id }}"
|
||||
onclick="toggleAllMaterialUsed('{{ intervention.id }}', '{{ order.id }}')"
|
||||
{% if not can_edit_intervention %}disabled{% endif %}
|
||||
style="font-size: 0.75rem;">
|
||||
<i class="bi bi-check-all fs-6"></i>
|
||||
<span class="btn-order-toggle-all-text" id="btn-mobile-order-toggle-all-text-{{ order.id }}">
|
||||
{% if order.all_items_used %}{% translate "Tout décocher" %}{% else %}{% translate "Tout cocher" %}{% endif %}
|
||||
</span>
|
||||
</button>
|
||||
<span class="badge bg-white text-dark border font-monospace" id="mobile-order-stats-badge-{{ order.id }}" style="font-size: 0.75rem;">
|
||||
<span id="mobile-order-used-count-{{ order.id }}">{{ order.used_items_count }}</span> / {{ order.total_items_count }} {% translate "utilisé(s)" %}
|
||||
</span>
|
||||
</div>
|
||||
{% for item in items %}
|
||||
<div class="list-group-item px-3 py-2 d-flex align-items-center justify-content-between gap-2">
|
||||
<div class="d-flex align-items-center gap-2 flex-grow-1" style="min-width: 0;">
|
||||
|
|
@ -6933,6 +6937,18 @@ function updateMaterialUI(data) {
|
|||
el.textContent = data.all_used ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
|
||||
if (data.order_id) {
|
||||
const orderAllUsed = data.order_all_used !== undefined ? data.order_all_used : data.all_used;
|
||||
const orderBtnTexts = document.querySelectorAll(`#btn-order-toggle-all-text-${data.order_id}, #btn-mobile-order-toggle-all-text-${data.order_id}`);
|
||||
orderBtnTexts.forEach(el => {
|
||||
el.textContent = orderAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
if (data.order_used_count !== undefined) {
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${data.order_id}, #mobile-order-used-count-${data.order_id}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = data.order_used_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.item_id !== undefined && data.is_other !== undefined) {
|
||||
const prefix = data.is_other ? 'other-' : 'item-';
|
||||
const badges = document.querySelectorAll(`#material-badge-${prefix}${data.item_id}, #mobile-material-item-badge-${prefix}${data.item_id}`);
|
||||
|
|
@ -6995,7 +7011,7 @@ function toggleAllMaterialUsed(interventionId, orderId) {
|
|||
|| document.cookie.split('; ').find(r => r.startsWith('csrftoken='))?.split('=')[1]
|
||||
|| '';
|
||||
|
||||
const buttons = document.querySelectorAll('#btn-desktop-toggle-all-material, #btn-mobile-toggle-all-material');
|
||||
const buttons = document.querySelectorAll('#btn-desktop-toggle-all-material, #btn-mobile-toggle-all-material, .btn-order-toggle-all');
|
||||
buttons.forEach(b => b.disabled = true);
|
||||
|
||||
const formData = new FormData();
|
||||
|
|
@ -7037,6 +7053,27 @@ function toggleAllMaterialUsed(interventionId, orderId) {
|
|||
});
|
||||
});
|
||||
|
||||
if (orderId) {
|
||||
const orderBtnTexts = document.querySelectorAll(`#btn-order-toggle-all-text-${orderId}, #btn-mobile-order-toggle-all-text-${orderId}`);
|
||||
orderBtnTexts.forEach(el => {
|
||||
el.textContent = isAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
if (data.order_used_count !== undefined) {
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${orderId}, #mobile-order-used-count-${orderId}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = data.order_used_count);
|
||||
}
|
||||
} else {
|
||||
document.querySelectorAll('.btn-order-toggle-all-text').forEach(el => {
|
||||
el.textContent = isAllUsed ? "{% translate 'Tout décocher' %}" : "{% translate 'Tout cocher' %}";
|
||||
});
|
||||
document.querySelectorAll('.btn-order-toggle-all').forEach(btn => {
|
||||
const id = btn.id.replace('btn-order-toggle-all-', '').replace('btn-mobile-order-toggle-all-', '');
|
||||
const totalCheckboxes = document.querySelectorAll(`.material-used-checkbox[data-order-id="${id}"]`).length;
|
||||
const orderUsedCountEls = document.querySelectorAll(`#order-used-count-${id}, #mobile-order-used-count-${id}`);
|
||||
orderUsedCountEls.forEach(el => el.textContent = isAllUsed ? totalCheckboxes : 0);
|
||||
});
|
||||
}
|
||||
|
||||
updateMaterialUI({
|
||||
used_count: data.used_count,
|
||||
total_count: data.total_count,
|
||||
|
|
|
|||
|
|
@ -3834,6 +3834,8 @@ def toggle_material_used(request, intervention_id, item_id):
|
|||
'is_used': item.is_used,
|
||||
'order_id': order.id,
|
||||
'order_all_used': order_all_used,
|
||||
'order_used_count': order.used_items_count,
|
||||
'order_total_count': order.total_items_count,
|
||||
'used_count': stats['used'],
|
||||
'total_count': stats['total'],
|
||||
'all_used': stats['all_used'],
|
||||
|
|
@ -3891,5 +3893,7 @@ def toggle_all_material_used(request, intervention_id):
|
|||
'used_count': stats['used'],
|
||||
'total_count': stats['total'],
|
||||
'order_id': int(order_id) if order_id else None,
|
||||
'order_used_count': total_target if target_state else 0,
|
||||
'order_total_count': total_target,
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue