feat(assets): align assets toolbar layout with controls and enable multi-page select all
This commit is contained in:
parent
f19d09ce5f
commit
9c9aa30df7
3 changed files with 442 additions and 132 deletions
|
|
@ -638,6 +638,79 @@ function applyVisibleColumnsPreference(visibleColumns) {
|
|||
}
|
||||
});
|
||||
isApplyingSavedColumnVisibility = false;
|
||||
|
||||
// Update checkboxes in dropdown
|
||||
document.querySelectorAll('.col-toggle-check').forEach(cb => {
|
||||
cb.checked = wanted.has(cb.dataset.column);
|
||||
});
|
||||
}
|
||||
|
||||
function renderColumnsDropdown() {
|
||||
const container = document.getElementById('columns-list-container');
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
|
||||
const cols = [
|
||||
{ field: 'code', label: gettext('Code'), defaultVisible: true },
|
||||
{ field: 'name', label: gettext('Nom'), defaultVisible: true },
|
||||
{ field: 'element_type', label: gettext('Type'), defaultVisible: true },
|
||||
{ field: 'thematic_name', label: gettext('Thématique'), defaultVisible: true },
|
||||
{ field: 'category', label: gettext('Catégorie d\'assets'), defaultVisible: false },
|
||||
{ field: 'model', label: gettext('Modèle'), defaultVisible: false },
|
||||
{ field: 'status', label: gettext('Statut'), defaultVisible: false },
|
||||
{ field: 'validation_status', label: gettext('Statut de validation'), defaultVisible: true },
|
||||
];
|
||||
|
||||
const visibleCols = new Set(getVisibleColumnsPreference());
|
||||
|
||||
cols.forEach(col => {
|
||||
const isVisible = visibleCols.has(col.field);
|
||||
const div = document.createElement('div');
|
||||
div.className = 'form-check py-1';
|
||||
div.innerHTML = `
|
||||
<input class="form-check-input col-toggle-check" type="checkbox" id="col-toggle-${col.field}" data-column="${col.field}" ${isVisible ? 'checked' : ''}>
|
||||
<label class="form-check-label user-select-none small text-dark" for="col-toggle-${col.field}">${col.label}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
container.addEventListener('change', (e) => {
|
||||
const cb = e.target.closest('.col-toggle-check');
|
||||
if (!cb) return;
|
||||
const colField = cb.dataset.column;
|
||||
if (cb.checked) {
|
||||
$('#unified-table').bootstrapTable('showColumn', colField);
|
||||
} else {
|
||||
$('#unified-table').bootstrapTable('hideColumn', colField);
|
||||
}
|
||||
schedulePreferencesSave();
|
||||
});
|
||||
|
||||
document.getElementById('cols-select-all')?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
cols.forEach(col => {
|
||||
$('#unified-table').bootstrapTable('showColumn', col.field);
|
||||
});
|
||||
container.querySelectorAll('.col-toggle-check').forEach(cb => { cb.checked = true; });
|
||||
schedulePreferencesSave();
|
||||
});
|
||||
|
||||
document.getElementById('cols-reset-default')?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
cols.forEach(col => {
|
||||
if (col.defaultVisible) {
|
||||
$('#unified-table').bootstrapTable('showColumn', col.field);
|
||||
} else {
|
||||
$('#unified-table').bootstrapTable('hideColumn', col.field);
|
||||
}
|
||||
});
|
||||
container.querySelectorAll('.col-toggle-check').forEach(cb => {
|
||||
const field = cb.dataset.column;
|
||||
const def = cols.find(c => c.field === field);
|
||||
cb.checked = !!def?.defaultVisible;
|
||||
});
|
||||
schedulePreferencesSave();
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Preferences: save / restore ─────────────────────────────────────────────
|
||||
|
|
@ -834,14 +907,27 @@ $(document).ready(function () {
|
|||
search: false,
|
||||
pagination: true,
|
||||
pageSize: 100,
|
||||
maintainMetaData: true,
|
||||
clickToSelect: true,
|
||||
ignoreClickToSelectOn: function (element) {
|
||||
return $(element).closest('.zoom-btn, .unified-code-link').length > 0;
|
||||
},
|
||||
onCheck: function (row) { row.state = true; selectedIds.add(row.id); updateSelectionStatus(); },
|
||||
onUncheck: function (row) { row.state = false; selectedIds.delete(row.id); updateSelectionStatus(); },
|
||||
onCheckAll: function (rows) { rows.forEach(r => { r.state = true; selectedIds.add(r.id); }); updateSelectionStatus(); },
|
||||
onUncheckAll: function (rows) { rows.forEach(r => { r.state = false; selectedIds.delete(r.id); }); updateSelectionStatus(); },
|
||||
onCheck: function (row) {
|
||||
row.state = true;
|
||||
selectedIds.add(row.id);
|
||||
updateSelectionStatus();
|
||||
},
|
||||
onUncheck: function (row) {
|
||||
row.state = false;
|
||||
selectedIds.delete(row.id);
|
||||
updateSelectionStatus();
|
||||
},
|
||||
onCheckAll: function (rows) {
|
||||
selectAllTableRows();
|
||||
},
|
||||
onUncheckAll: function (rows) {
|
||||
deselectAllTableRows();
|
||||
},
|
||||
onClickRow: function (row) {
|
||||
if (!row.lon || !row.lat) return;
|
||||
const source = row.is_management_zone ? 'management_zones' : (row.is_location ? 'locations' : 'assets');
|
||||
|
|
@ -851,6 +937,10 @@ $(document).ready(function () {
|
|||
},
|
||||
});
|
||||
|
||||
$('#unified-table').on('post-body.bs.table page-change.bs.table', function () {
|
||||
syncSelectAllCheckbox();
|
||||
});
|
||||
|
||||
$(window).on('resize', function () {
|
||||
$('#unified-table').bootstrapTable('resetView', { height: calcHeight() });
|
||||
});
|
||||
|
|
@ -1289,28 +1379,21 @@ $(document).ready(function () {
|
|||
|
||||
// ─── Select all / deselect all ─────────────────────────────────────────
|
||||
|
||||
function selectAllTableRows() {
|
||||
const allRows = $('#unified-table').bootstrapTable('getData') || [];
|
||||
allRows.forEach(r => {
|
||||
selectedIds.add(r.id);
|
||||
r.state = true;
|
||||
});
|
||||
$('#unified-table').bootstrapTable('checkAll');
|
||||
updateSelectionStatus();
|
||||
}
|
||||
$(document).on('click', 'input[name="btSelectAll"]', function () {
|
||||
if (this.checked) {
|
||||
selectAllTableRows();
|
||||
} else {
|
||||
deselectAllTableRows();
|
||||
}
|
||||
});
|
||||
|
||||
function deselectAllTableRows() {
|
||||
const allRows = $('#unified-table').bootstrapTable('getData') || [];
|
||||
allRows.forEach(r => {
|
||||
r.state = false;
|
||||
});
|
||||
selectedIds.clear();
|
||||
$('#unified-table').bootstrapTable('uncheckAll');
|
||||
updateSelectionStatus();
|
||||
}
|
||||
document.getElementById('page-size-select')?.addEventListener('change', function () {
|
||||
const newSize = parseInt(this.value, 10) || 100;
|
||||
$('#unified-table').bootstrapTable('refreshOptions', { pageSize: newSize });
|
||||
});
|
||||
|
||||
document.getElementById('unified-select-all').addEventListener('click', selectAllTableRows);
|
||||
document.getElementById('unified-deselect-all').addEventListener('click', deselectAllTableRows);
|
||||
document.getElementById('unified-select-all')?.addEventListener('click', selectAllTableRows);
|
||||
document.getElementById('unified-deselect-all')?.addEventListener('click', deselectAllTableRows);
|
||||
|
||||
bindUnifiedAssetsExport();
|
||||
bindUnifiedBulkActions();
|
||||
|
|
@ -1349,6 +1432,7 @@ $(document).ready(function () {
|
|||
// ─── Init ──────────────────────────────────────────────────────────────
|
||||
|
||||
applyInitialPreferences();
|
||||
renderColumnsDropdown();
|
||||
buildCategoryLookup();
|
||||
renderCategoryFilters();
|
||||
renderModelFilters();
|
||||
|
|
@ -1645,7 +1729,7 @@ function getCsrfToken() {
|
|||
}
|
||||
|
||||
function getSelectedAssetKeysForExport() {
|
||||
const rows = $('#unified-table').bootstrapTable('getData') || [];
|
||||
const rows = getAllTableRows();
|
||||
const keys = [];
|
||||
const seen = new Set();
|
||||
|
||||
|
|
@ -1740,7 +1824,7 @@ function bindUnifiedAssetsExport() {
|
|||
}
|
||||
|
||||
function buildBulkSelectedAssetsPayload() {
|
||||
const rows = $('#unified-table').bootstrapTable('getData') || [];
|
||||
const rows = getAllTableRows();
|
||||
const byId = new Map(rows.filter(r => !r.is_location).map(r => [String(r.id), r]));
|
||||
const payload = [];
|
||||
|
||||
|
|
@ -2774,14 +2858,9 @@ function loadTableData(features) {
|
|||
$('#unified-spinner').hide();
|
||||
$('#unified-table').bootstrapTable('load', rows);
|
||||
|
||||
// Restore checked state
|
||||
if (selectedIds.size) {
|
||||
rows.forEach((row, idx) => {
|
||||
if (selectedIds.has(row.id)) {
|
||||
$('#unified-table').bootstrapTable('check', idx);
|
||||
}
|
||||
});
|
||||
}
|
||||
updateSelectionStatus();
|
||||
const countBadge = document.getElementById('assets-counter-badge');
|
||||
if (countBadge) countBadge.textContent = rows.length;
|
||||
}
|
||||
|
||||
// ─── Map integration ──────────────────────────────────────────────────────────
|
||||
|
|
@ -3093,6 +3172,9 @@ function showEmptyState() {
|
|||
hideSpinner();
|
||||
document.getElementById('unified-empty-state').style.display = '';
|
||||
updateAssetsHint();
|
||||
const countBadge = document.getElementById('assets-counter-badge');
|
||||
if (countBadge) countBadge.textContent = '0';
|
||||
syncSelectAllCheckbox();
|
||||
}
|
||||
|
||||
function hideEmptyState() {
|
||||
|
|
@ -3129,6 +3211,81 @@ function updateFilterBadge() {
|
|||
badge.classList.toggle('d-none', n === 0);
|
||||
}
|
||||
|
||||
function getAllTableRows() {
|
||||
const $table = $('#unified-table');
|
||||
if (!$table.length || typeof $table.bootstrapTable !== 'function') return [];
|
||||
const options = $table.bootstrapTable('getOptions');
|
||||
if (options && Array.isArray(options.data) && options.data.length > 0) {
|
||||
return options.data;
|
||||
}
|
||||
return $table.bootstrapTable('getData', { useCurrentPage: false }) || [];
|
||||
}
|
||||
|
||||
var isBulkSelectionInProgress = false;
|
||||
|
||||
function selectAllTableRows() {
|
||||
if (isBulkSelectionInProgress) return;
|
||||
isBulkSelectionInProgress = true;
|
||||
try {
|
||||
const allRows = getAllTableRows();
|
||||
allRows.forEach(r => {
|
||||
selectedIds.add(r.id);
|
||||
r.state = true;
|
||||
});
|
||||
$('#unified-table tbody tr').addClass('selected');
|
||||
$('#unified-table input[name="btSelectItem"]').prop('checked', true);
|
||||
updateSelectionStatus();
|
||||
} finally {
|
||||
isBulkSelectionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
function deselectAllTableRows() {
|
||||
if (isBulkSelectionInProgress) return;
|
||||
isBulkSelectionInProgress = true;
|
||||
try {
|
||||
const allRows = getAllTableRows();
|
||||
allRows.forEach(r => {
|
||||
r.state = false;
|
||||
selectedIds.delete(r.id);
|
||||
});
|
||||
$('#unified-table tbody tr').removeClass('selected');
|
||||
$('#unified-table input[name="btSelectItem"]').prop('checked', false);
|
||||
updateSelectionStatus();
|
||||
} finally {
|
||||
isBulkSelectionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
function syncSelectAllCheckbox() {
|
||||
const allRows = getAllTableRows();
|
||||
const selectAllCheckboxes = document.querySelectorAll('input[name="btSelectAll"]');
|
||||
if (!selectAllCheckboxes.length) return;
|
||||
|
||||
if (allRows.length === 0) {
|
||||
selectAllCheckboxes.forEach(cb => {
|
||||
cb.checked = false;
|
||||
cb.indeterminate = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedRowCount = 0;
|
||||
allRows.forEach(r => {
|
||||
if (selectedIds.has(r.id)) {
|
||||
selectedRowCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const isAllSelected = selectedRowCount === allRows.length;
|
||||
const isSomeSelected = selectedRowCount > 0 && selectedRowCount < allRows.length;
|
||||
|
||||
selectAllCheckboxes.forEach(cb => {
|
||||
cb.checked = isAllSelected;
|
||||
cb.indeterminate = isSomeSelected;
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelectionStatus() {
|
||||
const el = document.getElementById('unified-selection-status');
|
||||
if (el) el.textContent = `${selectedIds.size} ${gettext('sélectionné(s)')}`;
|
||||
|
|
@ -3137,10 +3294,11 @@ function updateSelectionStatus() {
|
|||
if (hiddenInput) hiddenInput.value = Array.from(selectedIds).join(',');
|
||||
const hiddenLocationsInput = document.getElementById('preventive-selected-locations-ids');
|
||||
if (hiddenLocationsInput) hiddenLocationsInput.value = Array.from(selectedIds).join(',');
|
||||
syncSelectAllCheckbox();
|
||||
}
|
||||
|
||||
function buildPreventiveSelectedAssetsPayload() {
|
||||
const rows = $('#unified-table').bootstrapTable('getData') || [];
|
||||
const rows = getAllTableRows();
|
||||
const byId = new Map(rows.filter(r => !r.is_location).map(r => [String(r.id), r]));
|
||||
const payload = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -390,8 +390,8 @@
|
|||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
top: 2px !important;
|
||||
right: -6px !important;
|
||||
top: -5px !important;
|
||||
right: -7px !important;
|
||||
left: auto !important;
|
||||
transform: none !important;
|
||||
min-width: 1.25rem;
|
||||
|
|
@ -479,7 +479,7 @@
|
|||
font-weight: 600 !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.04em !important;
|
||||
border-bottom: 1.5px solid #e2e8f0 !important;
|
||||
border-bottom: 2px solid #cbd5e1 !important;
|
||||
padding: 0.45rem 0.65rem !important;
|
||||
vertical-align: middle !important;
|
||||
white-space: nowrap !important;
|
||||
|
|
@ -494,11 +494,11 @@
|
|||
|
||||
/* Rows & Cells */
|
||||
.bootstrap-table.bootstrap5 #unified-table td {
|
||||
padding: 0.32rem 0.65rem !important;
|
||||
padding: 0.35rem 0.65rem !important;
|
||||
vertical-align: middle !important;
|
||||
font-size: 0.82rem !important;
|
||||
color: #334155;
|
||||
border-bottom: 1px solid #f1f5f9 !important;
|
||||
border-bottom: 1px solid #e2e8f0 !important;
|
||||
line-height: 1.3 !important;
|
||||
}
|
||||
|
||||
|
|
@ -593,43 +593,157 @@
|
|||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Selection toolbar styling */
|
||||
#unified-select-all,
|
||||
#unified-deselect-all {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #475569;
|
||||
border-radius: 6px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
padding: 0.28rem 0.65rem;
|
||||
/* ─── Controls/Assets Tabs ─────────────────────────────────────────── */
|
||||
.controls-tabs {
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
}
|
||||
.controls-tabs .nav-link {
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: #64748b;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-bottom: -2px;
|
||||
background: transparent;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
#unified-select-all:hover,
|
||||
#unified-deselect-all:hover {
|
||||
background-color: #f1f5f9;
|
||||
.controls-tabs .nav-link:hover {
|
||||
color: #0f172a;
|
||||
border-color: #cbd5e1;
|
||||
border-bottom-color: #cbd5e1;
|
||||
}
|
||||
.controls-tabs .nav-link.active {
|
||||
color: #0f172a;
|
||||
border-bottom-color: #0f172a;
|
||||
background: transparent;
|
||||
}
|
||||
.controls-tabs .tab-counter-badge {
|
||||
background-color: #0f172a;
|
||||
color: #ffffff;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
padding: 0.18rem 0.55rem;
|
||||
border-radius: 9999px;
|
||||
margin-left: 0.5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
border: 1px solid #0f172a;
|
||||
}
|
||||
|
||||
/* ─── Assets Toolbar ───────────────────────────────────────────────── */
|
||||
.assets-toolbar {
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.filter-trigger-btn,
|
||||
#columns-dropdown-btn,
|
||||
#action-dropdown-btn {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
padding: 0.32rem 0.75rem;
|
||||
background-color: #ffffff;
|
||||
color: #334155;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
transition: all 0.15s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
.filter-trigger-btn:hover,
|
||||
#columns-dropdown-btn:hover,
|
||||
#action-dropdown-btn:hover {
|
||||
background-color: #f8fafc;
|
||||
border-color: #94a3b8;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.btn-new-asset {
|
||||
background-color: #0f172a;
|
||||
color: #ffffff !important;
|
||||
border: 1px solid #0f172a;
|
||||
border-radius: 8px;
|
||||
padding: 0.32rem 0.75rem;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
transition: all 0.15s ease;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-new-asset:hover:not(:disabled) {
|
||||
background-color: #1e293b;
|
||||
color: #ffffff !important;
|
||||
border-color: #1e293b;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
.btn-new-asset:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.columns-dropdown-menu {
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.12), 0 8px 10px -6px rgba(0, 0, 0, 0.08);
|
||||
min-width: 250px;
|
||||
z-index: 1060;
|
||||
}
|
||||
|
||||
.columns-checkbox-list {
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check {
|
||||
margin-bottom: 0.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check-input {
|
||||
cursor: pointer;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check-label {
|
||||
cursor: pointer;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.page-size-selector {
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
padding: 0.25rem 0.55rem;
|
||||
font-size: 0.82rem;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 7px;
|
||||
background-color: #ffffff;
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
.page-size-select:focus {
|
||||
outline: none;
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
#unified-selection-status {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
#unified-bulk-action-dropdown .dropdown-toggle {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #475569;
|
||||
border-radius: 6px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
padding: 0.28rem 0.75rem;
|
||||
}
|
||||
#unified-bulk-action-dropdown .dropdown-toggle:hover {
|
||||
background-color: #f1f5f9;
|
||||
color: #0f172a;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Pagination bar styling */
|
||||
.bootstrap-table.bootstrap5 .fixed-table-pagination {
|
||||
|
|
@ -1074,21 +1188,21 @@
|
|||
{# ─── Table column ───────────────────────────────────────────────── #}
|
||||
<div id="col-table" class="col-md-6 d-flex flex-column" style="max-height: calc(100vh - 130px);">
|
||||
|
||||
{# Tab nav with + and filter buttons #}
|
||||
<ul class="nav nav-tabs flex-shrink-0" id="tablesTab" role="tablist">
|
||||
<!-- Entête d'onglet -->
|
||||
<ul class="nav nav-tabs controls-tabs flex-shrink-0 mb-2" id="tablesTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" type="button">
|
||||
<button class="nav-link active" id="assets-table-tab" type="button">
|
||||
<i class="bi bi-layers me-1"></i>{% translate "Assets" %}
|
||||
<span class="tab-counter-badge" id="assets-counter-badge">0</span>
|
||||
</button>
|
||||
</li>
|
||||
<div class="my-1 ms-2">
|
||||
<button class="btn btn-light" type="button" id="btn-add-asset"
|
||||
title="{% translate 'Ajouter un asset' %}">
|
||||
<i class="bi bi-plus-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-1 ms-1">
|
||||
<button class="btn btn-light position-relative" type="button"
|
||||
</ul>
|
||||
|
||||
<!-- Barre d'outils du tableau : Filtres, Colonnes, Action, Nouvel asset, Statut de sélection, Affichage par page -->
|
||||
<div class="assets-toolbar d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
|
||||
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||
<!-- Bouton Filtres avec badge -->
|
||||
<button class="btn btn-light position-relative filter-trigger-btn" type="button"
|
||||
data-bs-toggle="modal" data-bs-target="#filterModal"
|
||||
title="{% translate 'Filtres' %}">
|
||||
<i class="bi bi-funnel"></i> {% translate "Filtres" %}
|
||||
|
|
@ -1096,53 +1210,90 @@
|
|||
<span id="filter-count">0</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown Sélecteur de Colonnes -->
|
||||
<div class="dropdown d-inline-block">
|
||||
<button type="button" class="btn btn-light dropdown-toggle" id="columns-dropdown-btn" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" title="{% translate 'Choisir les colonnes à afficher' %}">
|
||||
<i class="bi bi-layout-three-columns me-1"></i>{% translate "Colonnes" %}
|
||||
</button>
|
||||
<div class="dropdown-menu shadow-sm py-2 px-3 columns-dropdown-menu" aria-labelledby="columns-dropdown-btn">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-1 border-bottom">
|
||||
<span class="fw-semibold text-dark small">{% translate "Colonnes" %}</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none text-primary small" id="cols-select-all">{% translate "Toutes" %}</button>
|
||||
<span class="text-muted small">·</span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none text-muted small" id="cols-reset-default">{% translate "Défaut" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="columns-list-container" class="columns-checkbox-list">
|
||||
<!-- Injecté dynamiquement par JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Action (remplace Excel) -->
|
||||
{% if not preventive_draft_id %}
|
||||
<div class="dropdown d-inline-block" id="unified-bulk-action-dropdown">
|
||||
<button class="btn btn-light dropdown-toggle" id="action-dropdown-btn" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="{% translate 'Actions sur les assets sélectionnés' %}">
|
||||
<i class="bi bi-gear me-1"></i>{% translate "Action" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu shadow-sm py-2">
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-export"
|
||||
data-bs-toggle="modal" data-bs-target="#unifiedExportModal">
|
||||
<i class="bi bi-download me-2"></i>{% translate "Exporter" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if has_bulk_edit_status_permission %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-bulk-status"
|
||||
data-bs-toggle="modal" data-bs-target="#bulkStatusModal">
|
||||
<i class="bi bi-pencil-square me-2"></i>{% translate "Modifier le statut en masse" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if has_bulk_edit_validation_status_permission %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-bulk-validation-status"
|
||||
data-bs-toggle="modal" data-bs-target="#bulkValidationStatusModal">
|
||||
<i class="bi bi-shield-check me-2"></i>{% translate "Modifier le statut de validation en masse" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Bouton Nouvel Asset -->
|
||||
<button class="btn btn-new-asset" type="button" id="btn-add-asset" title="{% translate 'Ajouter un asset' %}">
|
||||
<i class="bi bi-plus-lg me-1"></i>
|
||||
<span>{% translate "Nouvel asset" %}</span>
|
||||
</button>
|
||||
|
||||
<!-- Statut sélection -->
|
||||
<span id="unified-selection-status" class="text-muted small ms-1 mb-0">0 {% translate "sélectionné(s)" %}</span>
|
||||
|
||||
{% if preventive_draft_id %}
|
||||
<button type="submit" class="btn btn-success btn-sm ms-2" form="preventive-select-form">{% translate "Valider la sélection" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="page-size-selector d-flex align-items-center gap-1">
|
||||
<label for="page-size-select" class="small text-muted mb-0 d-none d-sm-inline">{% translate "Afficher par" %}</label>
|
||||
<select id="page-size-select" class="form-select form-select-sm page-size-select">
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100" selected>100</option>
|
||||
<option value="200">200</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Tab content #}
|
||||
<div class="tab-content d-flex flex-column" style="flex: 1; min-height: 0;">
|
||||
<div class="tab-pane fade show active d-flex flex-column" style="flex: 1; min-height: 0; padding-top: 0.5rem;">
|
||||
|
||||
{# Selection toolbar #}
|
||||
<div class="d-flex gap-2 flex-wrap align-items-center mb-1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="unified-select-all">{% translate "Tout sélectionner" %}</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="unified-deselect-all">{% translate "Tout désélectionner" %}</button>
|
||||
{% if preventive_draft_id %}
|
||||
<button type="submit" class="btn btn-success btn-sm" form="preventive-select-form">{% translate "Valider la sélection" %}</button>
|
||||
{% endif %}
|
||||
<div id="unified-selection-status" class="text-muted small mb-0">0 {% translate "sélectionné(s)" %}</div>
|
||||
{% if not preventive_draft_id %}
|
||||
<div class="dropdown ms-auto" id="unified-bulk-action-dropdown">
|
||||
<button class="btn btn-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{% translate "Action" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-export"
|
||||
data-bs-toggle="modal" data-bs-target="#unifiedExportModal">
|
||||
<i class="bi bi-download me-2"></i>{% translate "Exporter" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if has_bulk_edit_status_permission %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-bulk-status"
|
||||
data-bs-toggle="modal" data-bs-target="#bulkStatusModal">
|
||||
<i class="bi bi-pencil-square me-2"></i>{% translate "Modifier le statut en masse" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if has_bulk_edit_validation_status_permission %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="unified-action-bulk-validation-status"
|
||||
data-bs-toggle="modal" data-bs-target="#bulkValidationStatusModal">
|
||||
<i class="bi bi-shield-check me-2"></i>{% translate "Modifier le statut de validation en masse" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tab-pane fade show active d-flex flex-column" style="flex: 1; min-height: 0;">
|
||||
|
||||
{# Table area: spinner overlay + empty state + table #}
|
||||
<div style="position: relative;">
|
||||
|
|
@ -1165,8 +1316,9 @@
|
|||
class="table table-hover table-sm"
|
||||
data-pagination="true"
|
||||
data-page-size="100"
|
||||
data-maintain-meta-data="true"
|
||||
data-click-to-select="true"
|
||||
data-show-columns="true"
|
||||
data-show-columns="false"
|
||||
data-search="false">
|
||||
</table>
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class ImportFulcrumTreesCommandTest(TestCase):
|
|||
"ges_conduite": "LI",
|
||||
"cf_sit": 4.5,
|
||||
"arb_anpla": "1995",
|
||||
"maj_date": "2024-05-10T00:00:00.000Z"
|
||||
"maj_date": "2026-05-10T00:00:00.000Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -96,8 +96,8 @@ class ImportFulcrumTreesCommandTest(TestCase):
|
|||
"sit_id": "099.01",
|
||||
"pos_id": "001",
|
||||
"dia_comma": "BC-TEST-001",
|
||||
"dia_date": "2024-05-10T00:00:00.000Z",
|
||||
"in_deadline": "2025-05-10T00:00:00.000Z",
|
||||
"dia_date": "2026-05-10T00:00:00.000Z",
|
||||
"in_deadline": "2027-05-10T00:00:00.000Z",
|
||||
"rec_delai": "Pla-1",
|
||||
"rec_interv": "FO",
|
||||
"in_fin_yn": "no",
|
||||
|
|
@ -149,23 +149,23 @@ class ImportFulcrumTreesCommandTest(TestCase):
|
|||
self.assertEqual(itv.priority, "3") # Pla-1 -> 3
|
||||
self.assertEqual(itv.status, "to_be_planned")
|
||||
self.assertIsNotNone(itv.expected_begin_time)
|
||||
self.assertEqual(itv.expected_begin_time.date(), date(2024, 5, 10))
|
||||
self.assertEqual(itv.expected_begin_time.date(), date(2026, 5, 10))
|
||||
self.assertIsNotNone(itv.expected_end_time)
|
||||
self.assertEqual(itv.expected_end_time.date(), date(2025, 5, 10))
|
||||
self.assertEqual(itv.expected_end_time.date(), date(2027, 5, 10))
|
||||
|
||||
# Vérifications dates de maintenance sur l'arbre
|
||||
self.assertEqual(tree.last_phytosanitary_date, date(2024, 5, 10))
|
||||
self.assertEqual(tree.next_inspection_date, date(2025, 5, 10))
|
||||
self.assertEqual(tree.last_phytosanitary_date, date(2026, 5, 10))
|
||||
self.assertEqual(tree.next_inspection_date, date(2027, 5, 10))
|
||||
self.assertEqual(tree.pruning_frequency_years, 1)
|
||||
|
||||
# Vérifications événements de planification (timeline)
|
||||
self.assertEqual(itv.planned_events.count(), 2)
|
||||
begin_event = itv.planned_events.filter(event_type='expected_begin').first()
|
||||
self.assertIsNotNone(begin_event)
|
||||
self.assertEqual(begin_event.event_time.date(), date(2024, 5, 10))
|
||||
self.assertEqual(begin_event.event_time.date(), date(2026, 5, 10))
|
||||
end_event = itv.planned_events.filter(event_type='expected_end').first()
|
||||
self.assertIsNotNone(end_event)
|
||||
self.assertEqual(end_event.event_time.date(), date(2025, 5, 10))
|
||||
self.assertEqual(end_event.event_time.date(), date(2027, 5, 10))
|
||||
|
||||
# Vérification liaison InterventionAsset
|
||||
links = InterventionAsset.objects.filter(intervention=itv)
|
||||
|
|
|
|||
Loading…
Reference in a new issue