163 lines
6.7 KiB
JavaScript
163 lines
6.7 KiB
JavaScript
(() => {
|
|
function initialize() {
|
|
const table = document.getElementById('asset-table');
|
|
const openButton = document.getElementById('duplicate-search-button');
|
|
const dialog = document.getElementById('duplicate-search-dialog');
|
|
const select = document.getElementById('duplicate-field-select');
|
|
const applyButton = document.getElementById('duplicate-apply-button');
|
|
const clearButton = document.getElementById('duplicate-clear-button');
|
|
const ignoreEmpty = document.getElementById('duplicate-ignore-empty');
|
|
const caseInsensitive = document.getElementById('duplicate-case-insensitive');
|
|
const summary = document.getElementById('duplicate-search-summary');
|
|
const contextStatus = document.getElementById('asset-context-filter-status');
|
|
const duplicateChip = document.getElementById('asset-duplicate-filter-chip');
|
|
const duplicateLabel = document.getElementById('asset-duplicate-filter-label');
|
|
const duplicateRemove = document.getElementById('asset-duplicate-filter-remove');
|
|
const categoryRemove = document.getElementById('asset-category-filter-remove');
|
|
if (!table || !openButton || !dialog || table.dataset.duplicateSearchReady === '1') return;
|
|
table.dataset.duplicateSearchReady = '1';
|
|
|
|
const dataRows = () => [...table.tBodies[0].rows].filter(row => !row.querySelector('.empty-state'));
|
|
const normallyVisible = row => {
|
|
if (row.classList.contains('duplicate-hidden')) return false;
|
|
if (row.hidden || row.classList.contains('hidden')) return false;
|
|
const style = getComputedStyle(row);
|
|
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
};
|
|
const availableFields = () => [...table.querySelectorAll('thead tr:first-child th[data-field]')]
|
|
.filter(th => th.style.display !== 'none')
|
|
.map(th => ({
|
|
field: th.dataset.field,
|
|
label: th.dataset.label || th.textContent.trim()
|
|
}));
|
|
|
|
function refreshContextStatus() {
|
|
const categoryChip = document.getElementById('asset-category-filter-chip');
|
|
const duplicateActive = table.dataset.duplicateFilterActive === '1';
|
|
if (duplicateChip) duplicateChip.hidden = !duplicateActive;
|
|
if (contextStatus) {
|
|
const hasActive = Boolean(categoryChip) || duplicateActive;
|
|
contextStatus.hidden = !hasActive;
|
|
contextStatus.classList.toggle('is-active', hasActive);
|
|
}
|
|
}
|
|
|
|
function fillSelect() {
|
|
const previous = select.value;
|
|
select.innerHTML = '';
|
|
availableFields().forEach(item => {
|
|
const option = document.createElement('option');
|
|
option.value = item.field;
|
|
option.textContent = item.label;
|
|
select.appendChild(option);
|
|
});
|
|
if ([...select.options].some(option => option.value === previous)) {
|
|
select.value = previous;
|
|
}
|
|
}
|
|
|
|
function clearDuplicateFilter() {
|
|
dataRows().forEach(row => row.classList.remove('duplicate-hidden', 'duplicate-match'));
|
|
delete table.dataset.duplicateFilterActive;
|
|
delete table.dataset.duplicateFilterTotal;
|
|
delete table.dataset.duplicateFilterField;
|
|
if (summary) summary.hidden = true;
|
|
if (duplicateLabel) duplicateLabel.textContent = '';
|
|
refreshContextStatus();
|
|
window.dispatchEvent(new CustomEvent('asset-table-filter-changed', {detail: {table}}));
|
|
}
|
|
|
|
function normalize(value) {
|
|
const sharedKey = window.AssetManagerGermanSearchKey;
|
|
if (typeof sharedKey === 'function') {
|
|
return sharedKey(value, {
|
|
caseSensitive: !caseInsensitive.checked,
|
|
collapseWhitespace: true
|
|
});
|
|
}
|
|
let result = String(value || '').trim().replace(/\s+/g, ' ');
|
|
if (caseInsensitive.checked) result = result.toLocaleLowerCase();
|
|
return result;
|
|
}
|
|
|
|
function applyDuplicateFilter() {
|
|
// Remove only our previous result first. Normal column filters stay intact.
|
|
dataRows().forEach(row => row.classList.remove('duplicate-hidden', 'duplicate-match'));
|
|
const field = select.value;
|
|
if (!field) return;
|
|
|
|
const candidates = dataRows().filter(normallyVisible);
|
|
const groups = new Map();
|
|
candidates.forEach(row => {
|
|
const cell = row.querySelector(`td[data-field="${CSS.escape(field)}"]`);
|
|
const value = normalize(cell?.textContent || '');
|
|
if (ignoreEmpty.checked && !value) return;
|
|
const rows = groups.get(value) || [];
|
|
rows.push(row);
|
|
groups.set(value, rows);
|
|
});
|
|
|
|
const duplicateRows = new Set();
|
|
let duplicateGroups = 0;
|
|
groups.forEach(rows => {
|
|
if (rows.length > 1) {
|
|
duplicateGroups += 1;
|
|
rows.forEach(row => duplicateRows.add(row));
|
|
}
|
|
});
|
|
|
|
candidates.forEach(row => {
|
|
if (duplicateRows.has(row)) {
|
|
row.classList.add('duplicate-match');
|
|
} else {
|
|
row.classList.add('duplicate-hidden');
|
|
}
|
|
});
|
|
|
|
table.dataset.duplicateFilterActive = '1';
|
|
table.dataset.duplicateFilterTotal = String(duplicateRows.size);
|
|
table.dataset.duplicateFilterField = field;
|
|
|
|
const selectedOption = select.options[select.selectedIndex];
|
|
const fieldLabel = selectedOption?.textContent?.trim() || field;
|
|
const duplicateTemplate = contextStatus?.dataset.duplicateTemplate || 'Duplikate: {field}';
|
|
if (duplicateLabel) duplicateLabel.textContent = duplicateTemplate.replace('{field}', fieldLabel);
|
|
refreshContextStatus();
|
|
|
|
const summaryTemplate = dialog.dataset.resultSummaryTemplate || '{records} Datensätze in {groups} Dublettengruppen gefunden.';
|
|
if (summary) {
|
|
summary.textContent = summaryTemplate
|
|
.replace('{records}', String(duplicateRows.size))
|
|
.replace('{groups}', String(duplicateGroups));
|
|
summary.hidden = false;
|
|
}
|
|
window.dispatchEvent(new CustomEvent('asset-table-filter-changed', {detail: {table}}));
|
|
dialog.close();
|
|
}
|
|
|
|
openButton.addEventListener('click', () => {
|
|
// Search always starts from the current normal filter result, not from
|
|
// a previous duplicate result.
|
|
clearDuplicateFilter();
|
|
fillSelect();
|
|
dialog.showModal();
|
|
});
|
|
applyButton.addEventListener('click', applyDuplicateFilter);
|
|
clearButton.addEventListener('click', clearDuplicateFilter);
|
|
duplicateRemove?.addEventListener('click', clearDuplicateFilter);
|
|
categoryRemove?.addEventListener('click', () => {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.delete('category_id');
|
|
window.location.assign(url.toString());
|
|
});
|
|
refreshContextStatus();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initialize);
|
|
} else {
|
|
initialize();
|
|
}
|
|
window.addEventListener('asset-table-content-reloaded', initialize);
|
|
})();
|