feat: add dynamic active tab management and URL query param support for warehouse index view
This commit is contained in:
parent
20669d545b
commit
30051938d5
3 changed files with 55 additions and 13 deletions
|
|
@ -96,21 +96,21 @@
|
||||||
<div class="col-md-6" id="col-left" style="max-height: calc(100vh - 100px); overflow: auto;">
|
<div class="col-md-6" id="col-left" style="max-height: calc(100vh - 100px); overflow: auto;">
|
||||||
<ul class="nav nav-tabs" id="tablesTab" role="tablist">
|
<ul class="nav nav-tabs" id="tablesTab" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link active" id="warehouses-table-tab" data-bs-toggle="tab" data-bs-target="#warehouses-table-content" type="button" role="tab" aria-controls="warehouses-table" aria-selected="true" style="color: black;"><i class="bi bi-buildings"></i> {% translate "Entrepôts" %}</button>
|
<button class="nav-link {% if active_tab == 'warehouses' or not active_tab %}active{% endif %}" id="warehouses-table-tab" data-bs-toggle="tab" data-bs-target="#warehouses-table-content" type="button" role="tab" aria-controls="warehouses-table" aria-selected="{% if active_tab == 'warehouses' or not active_tab %}true{% else %}false{% endif %}" style="color: black;"><i class="bi bi-buildings"></i> {% translate "Entrepôts" %}</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="warehouse-locations-table-tab" data-bs-toggle="tab" data-bs-target="#warehouse-locations-table-content" type="button" role="tab" aria-controls="warehouse-locations-table" aria-selected="false" style="color: black;"><i class="bi bi-geo-alt"></i> {% translate "Emplacements" %}</button>
|
<button class="nav-link {% if active_tab == 'warehouse-locations' %}active{% endif %}" id="warehouse-locations-table-tab" data-bs-toggle="tab" data-bs-target="#warehouse-locations-table-content" type="button" role="tab" aria-controls="warehouse-locations-table" aria-selected="{% if active_tab == 'warehouse-locations' %}true{% else %}false{% endif %}" style="color: black;"><i class="bi bi-geo-alt"></i> {% translate "Emplacements" %}</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="products-table-tab" data-bs-toggle="tab" data-bs-target="#products-table-content" type="button" role="tab" aria-controls="products-table" aria-selected="false" style="color: black;"><i class="bi bi-box-seam"></i> {% translate "Produits" %}</button>
|
<button class="nav-link {% if active_tab == 'products' %}active{% endif %}" id="products-table-tab" data-bs-toggle="tab" data-bs-target="#products-table-content" type="button" role="tab" aria-controls="products-table" aria-selected="{% if active_tab == 'products' %}true{% else %}false{% endif %}" style="color: black;"><i class="bi bi-box-seam"></i> {% translate "Produits" %}</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="serialized-items-table-tab" data-bs-toggle="tab" data-bs-target="#serialized-items-table-content" type="button" role="tab" aria-controls="serialized-items-table" aria-selected="false" style="color: black;"><i class="bi bi-upc-scan"></i> {% translate "Articles tracés" %}</button>
|
<button class="nav-link {% if active_tab == 'serialized-items' %}active{% endif %}" id="serialized-items-table-tab" data-bs-toggle="tab" data-bs-target="#serialized-items-table-content" type="button" role="tab" aria-controls="serialized-items-table" aria-selected="{% if active_tab == 'serialized-items' %}true{% else %}false{% endif %}" style="color: black;"><i class="bi bi-upc-scan"></i> {% translate "Articles tracés" %}</button>
|
||||||
</li>
|
</li>
|
||||||
{# Onglet Conception masqué (modèle conservé, non supprimé) #}
|
{# Onglet Conception masqué (modèle conservé, non supprimé) #}
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-2" id="tablesTabContent">
|
<div class="tab-content my-2" id="tablesTabContent">
|
||||||
<div class="tab-pane fade show active" id="warehouses-table-content" role="tabpanel" aria-labelledby="warehouses-table-tab">
|
<div class="tab-pane fade {% if active_tab == 'warehouses' or not active_tab %}show active{% endif %}" id="warehouses-table-content" role="tabpanel" aria-labelledby="warehouses-table-tab">
|
||||||
<!-- Spinner -->
|
<!-- Spinner -->
|
||||||
<div id="warehouses-spinner" class="table-spinner">
|
<div id="warehouses-spinner" class="table-spinner">
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade show" id="warehouse-locations-table-content" role="tabpanel" aria-labelledby="warehouse-locations-table-tab">
|
<div class="tab-pane fade {% if active_tab == 'warehouse-locations' %}show active{% endif %}" id="warehouse-locations-table-content" role="tabpanel" aria-labelledby="warehouse-locations-table-tab">
|
||||||
<div id="warehouse-locations-table-container">
|
<div id="warehouse-locations-table-container">
|
||||||
<table id="warehouse-locations-table" class="table"
|
<table id="warehouse-locations-table" class="table"
|
||||||
data-height="500"
|
data-height="500"
|
||||||
|
|
@ -177,7 +177,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade show" id="products-table-content" role="tabpanel" aria-labelledby="products-table-tab">
|
<div class="tab-pane fade {% if active_tab == 'products' %}show active{% endif %}" id="products-table-content" role="tabpanel" aria-labelledby="products-table-tab">
|
||||||
<!-- Bouton ajout produit -->
|
<!-- Bouton ajout produit -->
|
||||||
{% if can_manage_products %}
|
{% if can_manage_products %}
|
||||||
<div class="d-flex justify-content-end mt-2 mb-1">
|
<div class="d-flex justify-content-end mt-2 mb-1">
|
||||||
|
|
@ -274,7 +274,7 @@
|
||||||
{# <div class="tab-pane fade show" id="designs-table-content" ...> ... </div> #}
|
{# <div class="tab-pane fade show" id="designs-table-content" ...> ... </div> #}
|
||||||
|
|
||||||
<!-- Onglet Articles tracés -->
|
<!-- Onglet Articles tracés -->
|
||||||
<div class="tab-pane fade" id="serialized-items-table-content" role="tabpanel" aria-labelledby="serialized-items-table-tab">
|
<div class="tab-pane fade {% if active_tab == 'serialized-items' %}show active{% endif %}" id="serialized-items-table-content" role="tabpanel" aria-labelledby="serialized-items-table-tab">
|
||||||
{% if can_manage_products %}
|
{% if can_manage_products %}
|
||||||
<div class="d-flex justify-content-end mt-2 mb-1">
|
<div class="d-flex justify-content-end mt-2 mb-1">
|
||||||
<a href="{% url 'stock:serialized_item_create' %}" class="btn btn-sm btn-primary">
|
<a href="{% url 'stock:serialized_item_create' %}" class="btn btn-sm btn-primary">
|
||||||
|
|
@ -284,7 +284,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form method="get" action="{% url 'stock:warehouses' %}" class="card mb-2 border-0 bg-light">
|
<form method="get" action="{% url 'stock:warehouses' %}" class="card mb-2 border-0 bg-light">
|
||||||
<input type="hidden" name="tab" value="serialized-items-table">
|
<input type="hidden" name="tab" value="serialized-items">
|
||||||
<div class="card-body py-2 row g-2 align-items-end">
|
<div class="card-body py-2 row g-2 align-items-end">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label fw-semibold small mb-1">{% translate "Recherche" %}</label>
|
<label class="form-label fw-semibold small mb-1">{% translate "Recherche" %}</label>
|
||||||
|
|
@ -313,7 +313,7 @@
|
||||||
<button type="submit" class="btn btn-sm btn-primary">
|
<button type="submit" class="btn btn-sm btn-primary">
|
||||||
<i class="bi bi-search"></i> {% translate "Filtrer" %}
|
<i class="bi bi-search"></i> {% translate "Filtrer" %}
|
||||||
</button>
|
</button>
|
||||||
<a href="{% url 'stock:warehouses' %}?tab=serialized-items-table" class="btn btn-sm btn-outline-secondary ms-1">
|
<a href="{% url 'stock:warehouses' %}?tab=serialized-items" class="btn btn-sm btn-outline-secondary ms-1">
|
||||||
<i class="bi bi-x-circle"></i> {% translate "Réinitialiser" %}
|
<i class="bi bi-x-circle"></i> {% translate "Réinitialiser" %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -446,14 +446,27 @@
|
||||||
document.querySelectorAll('#tablesTab button[data-bs-toggle="tab"]').forEach(function(btn) {
|
document.querySelectorAll('#tablesTab button[data-bs-toggle="tab"]').forEach(function(btn) {
|
||||||
btn.addEventListener('shown.bs.tab', function() {
|
btn.addEventListener('shown.bs.tab', function() {
|
||||||
const tabName = btn.id.replace('-table-tab', '');
|
const tabName = btn.id.replace('-table-tab', '');
|
||||||
history.replaceState(null, '', '?tab=' + tabName);
|
const currentUrl = new URL(window.location.href);
|
||||||
|
if (currentUrl.searchParams.get('tab') !== tabName) {
|
||||||
|
if (tabName === 'serialized-items') {
|
||||||
|
currentUrl.searchParams.set('tab', tabName);
|
||||||
|
} else {
|
||||||
|
currentUrl.search = '?tab=' + tabName;
|
||||||
|
}
|
||||||
|
history.replaceState(null, '', currentUrl.pathname + currentUrl.search);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Activer l'onglet indiqué par ?tab= au chargement de la page
|
// Activer l'onglet indiqué par ?tab= au chargement de la page
|
||||||
const tab = new URLSearchParams(window.location.search).get('tab');
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
let tab = urlParams.get('tab');
|
||||||
|
if (!tab && (urlParams.get('si_q') || urlParams.get('si_product') || urlParams.get('si_status'))) {
|
||||||
|
tab = 'serialized-items';
|
||||||
|
}
|
||||||
if (tab) {
|
if (tab) {
|
||||||
const tabBtn = document.getElementById(tab + '-table-tab');
|
const normalizedTab = tab.replace(/-table$/, '');
|
||||||
|
const tabBtn = document.getElementById(normalizedTab + '-table-tab') || document.getElementById(tab + '-table-tab');
|
||||||
if (tabBtn) {
|
if (tabBtn) {
|
||||||
tabBtn.click();
|
tabBtn.click();
|
||||||
tabBtn.blur();
|
tabBtn.blur();
|
||||||
|
|
|
||||||
|
|
@ -1162,6 +1162,26 @@ class ProductLocationsTests(TestCase):
|
||||||
self.assertContains(response, "SN-MOB-TEST")
|
self.assertContains(response, "SN-MOB-TEST")
|
||||||
self.assertContains(response, "editItemMobileModal")
|
self.assertContains(response, "editItemMobileModal")
|
||||||
|
|
||||||
|
def test_warehouses_index_serialized_items_tab_active(self):
|
||||||
|
response = self.client.get(reverse("stock:warehouses") + "?tab=serialized-items")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context["active_tab"], "serialized-items")
|
||||||
|
|
||||||
|
def test_warehouses_index_serialized_items_table_legacy_tab_active(self):
|
||||||
|
response = self.client.get(reverse("stock:warehouses") + "?tab=serialized-items-table")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context["active_tab"], "serialized-items")
|
||||||
|
|
||||||
|
def test_warehouses_index_filter_by_query_activates_serialized_items_tab(self):
|
||||||
|
response = self.client.get(reverse("stock:warehouses") + "?si_q=makita")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context["active_tab"], "serialized-items")
|
||||||
|
|
||||||
|
def test_warehouses_index_default_tab_is_warehouses(self):
|
||||||
|
response = self.client.get(reverse("stock:warehouses"))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context["active_tab"], "warehouses")
|
||||||
|
|
||||||
|
|
||||||
class AssociateStockLocationsCommandTests(TestCase):
|
class AssociateStockLocationsCommandTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ def warehouses_index(request):
|
||||||
si_query = request.GET.get("si_q", "").strip()
|
si_query = request.GET.get("si_q", "").strip()
|
||||||
si_product_id = request.GET.get("si_product", "").strip()
|
si_product_id = request.GET.get("si_product", "").strip()
|
||||||
si_status = request.GET.get("si_status", "").strip()
|
si_status = request.GET.get("si_status", "").strip()
|
||||||
|
tab_param = request.GET.get("tab", "").strip()
|
||||||
|
|
||||||
|
if tab_param in ("serialized-items", "serialized-items-table") or (not tab_param and (si_query or si_product_id or si_status)):
|
||||||
|
active_tab = "serialized-items"
|
||||||
|
elif tab_param in ("products", "warehouse-locations", "warehouses"):
|
||||||
|
active_tab = tab_param
|
||||||
|
else:
|
||||||
|
active_tab = "warehouses"
|
||||||
|
|
||||||
serialized_items_qs = SerializedItem.objects.select_related(
|
serialized_items_qs = SerializedItem.objects.select_related(
|
||||||
"product", "current_location__warehouse", "current_holder"
|
"product", "current_location__warehouse", "current_holder"
|
||||||
|
|
@ -129,6 +137,7 @@ def warehouses_index(request):
|
||||||
"si_product_id": si_product_id,
|
"si_product_id": si_product_id,
|
||||||
"si_status": si_status,
|
"si_status": si_status,
|
||||||
"si_status_choices": SerializedItem.STATUS_CHOICES,
|
"si_status_choices": SerializedItem.STATUS_CHOICES,
|
||||||
|
"active_tab": active_tab,
|
||||||
}
|
}
|
||||||
return render(request, "stock/warehouses_index.html", context)
|
return render(request, "stock/warehouses_index.html", context)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue