317 lines
12 KiB
JavaScript
317 lines
12 KiB
JavaScript
(() => {
|
||
'use strict';
|
||
|
||
const VIEW_STORAGE_KEY = 'vendoo_article_overview_view_v1';
|
||
const STATUS_FILTERS = [
|
||
{ value: '', label: 'Alle' },
|
||
{ value: 'active', label: 'Aktiv' },
|
||
{ value: 'reserved', label: 'Reserviert' },
|
||
{ value: 'sold', label: 'Verkauft' },
|
||
];
|
||
const CELL_LABELS = {
|
||
platform: 'Plattform',
|
||
provider: 'AI',
|
||
price: 'Preis',
|
||
status: 'Status',
|
||
location: 'Lagerort',
|
||
published: 'Veröffentlichung',
|
||
};
|
||
|
||
let historyObserver = null;
|
||
let metaObserver = null;
|
||
let refreshQueued = false;
|
||
|
||
function historySection() {
|
||
return document.getElementById('history');
|
||
}
|
||
|
||
function createButton(label, className, attributes = {}) {
|
||
const button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.className = className;
|
||
button.textContent = label;
|
||
for (const [name, value] of Object.entries(attributes)) button.setAttribute(name, value);
|
||
return button;
|
||
}
|
||
|
||
function currentView() {
|
||
const stored = localStorage.getItem(VIEW_STORAGE_KEY);
|
||
return stored === 'list' || stored === 'cards' ? stored : 'cards';
|
||
}
|
||
|
||
function applyView(mode, { persist = true } = {}) {
|
||
const section = historySection();
|
||
if (!section) return;
|
||
const normalized = mode === 'list' ? 'list' : 'cards';
|
||
section.dataset.articleView = normalized;
|
||
section.querySelectorAll('[data-article-view-choice]').forEach(button => {
|
||
const active = button.dataset.articleViewChoice === normalized;
|
||
button.classList.toggle('is-active', active);
|
||
button.setAttribute('aria-pressed', String(active));
|
||
});
|
||
if (persist) localStorage.setItem(VIEW_STORAGE_KEY, normalized);
|
||
}
|
||
|
||
function setStatusFilter(value) {
|
||
const select = document.getElementById('filter-status');
|
||
if (!select) return;
|
||
select.value = value;
|
||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||
queueRefresh();
|
||
}
|
||
|
||
function createOverviewToolbar() {
|
||
const section = historySection();
|
||
const filterbar = section?.querySelector('.history-filterbar');
|
||
if (!section || !filterbar || section.querySelector('.ux-article-toolbar')) return;
|
||
|
||
const toolbar = document.createElement('section');
|
||
toolbar.className = 'ux-article-toolbar';
|
||
toolbar.setAttribute('aria-label', 'Artikelansicht und Schnellfilter');
|
||
|
||
const summary = document.createElement('div');
|
||
summary.className = 'ux-article-summary';
|
||
const summaryTitle = document.createElement('strong');
|
||
summaryTitle.textContent = 'Artikelübersicht';
|
||
const summaryCount = document.createElement('span');
|
||
summaryCount.id = 'ux-article-result-count';
|
||
summaryCount.textContent = 'Artikel werden geladen …';
|
||
summary.append(summaryTitle, summaryCount);
|
||
|
||
const filters = document.createElement('div');
|
||
filters.className = 'ux-status-filters';
|
||
filters.setAttribute('role', 'group');
|
||
filters.setAttribute('aria-label', 'Status schnell filtern');
|
||
for (const filter of STATUS_FILTERS) {
|
||
const button = createButton(filter.label, 'ux-status-filter', {
|
||
'data-article-status': filter.value,
|
||
'aria-pressed': 'false',
|
||
});
|
||
button.addEventListener('click', () => setStatusFilter(filter.value));
|
||
filters.append(button);
|
||
}
|
||
|
||
const viewSwitch = document.createElement('div');
|
||
viewSwitch.className = 'ux-article-view-switch';
|
||
viewSwitch.setAttribute('role', 'group');
|
||
viewSwitch.setAttribute('aria-label', 'Darstellung wählen');
|
||
const cards = createButton('Karten', 'ux-view-choice', {
|
||
'data-article-view-choice': 'cards',
|
||
'aria-pressed': 'false',
|
||
});
|
||
const list = createButton('Liste', 'ux-view-choice', {
|
||
'data-article-view-choice': 'list',
|
||
'aria-pressed': 'false',
|
||
});
|
||
cards.addEventListener('click', () => applyView('cards'));
|
||
list.addEventListener('click', () => applyView('list'));
|
||
viewSwitch.append(cards, list);
|
||
|
||
toolbar.append(summary, filters, viewSwitch);
|
||
|
||
const activeFilters = document.createElement('div');
|
||
activeFilters.className = 'ux-active-filters';
|
||
activeFilters.id = 'ux-active-filters';
|
||
activeFilters.setAttribute('aria-live', 'polite');
|
||
|
||
filterbar.before(toolbar);
|
||
filterbar.after(activeFilters);
|
||
}
|
||
|
||
function enhanceBatchActions() {
|
||
const actions = document.querySelector('#history .history-batch-actions');
|
||
if (!actions || actions.querySelector('.ux-batch-primary')) return;
|
||
|
||
const primary = document.createElement('div');
|
||
primary.className = 'ux-batch-primary';
|
||
const more = document.createElement('details');
|
||
more.className = 'ux-batch-more';
|
||
const summary = document.createElement('summary');
|
||
summary.textContent = 'Weitere Aktionen';
|
||
const secondary = document.createElement('div');
|
||
secondary.className = 'ux-batch-secondary';
|
||
more.append(summary, secondary);
|
||
|
||
const primaryIds = ['history-publish-selected', 'history-edit-selected', 'history-bulk-status'];
|
||
const secondaryIds = ['history-price-selected', 'history-duplicate-selected', 'history-print-labels', 'history-delete-selected'];
|
||
for (const id of primaryIds) {
|
||
const button = document.getElementById(id);
|
||
if (button) primary.append(button);
|
||
}
|
||
for (const id of secondaryIds) {
|
||
const button = document.getElementById(id);
|
||
if (button) secondary.append(button);
|
||
}
|
||
actions.append(primary, more);
|
||
}
|
||
|
||
function annotateRows() {
|
||
const container = document.getElementById('history-list');
|
||
if (!container) return;
|
||
container.querySelectorAll('.history-row').forEach(row => {
|
||
for (const [column, label] of Object.entries(CELL_LABELS)) {
|
||
row.querySelector(`[data-column="${column}"]`)?.setAttribute('data-ux-label', label);
|
||
}
|
||
const title = row.querySelector('.history-article-title')?.textContent?.trim() || 'Artikel';
|
||
row.tabIndex = 0;
|
||
row.setAttribute('aria-label', `${title} öffnen`);
|
||
row.dataset.publishState = row.querySelector('.history-publish-empty') ? 'none' : 'available';
|
||
if (row.dataset.uxKeyboardBound !== 'true') {
|
||
row.dataset.uxKeyboardBound = 'true';
|
||
row.addEventListener('keydown', event => {
|
||
if ((event.key === 'Enter' || event.key === ' ') && event.target === row) {
|
||
event.preventDefault();
|
||
row.querySelector('.history-article-title')?.click();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
function syncStatusButtons() {
|
||
const value = document.getElementById('filter-status')?.value || '';
|
||
document.querySelectorAll('[data-article-status]').forEach(button => {
|
||
const active = button.dataset.articleStatus === value;
|
||
button.classList.toggle('is-active', active);
|
||
button.setAttribute('aria-pressed', String(active));
|
||
});
|
||
}
|
||
|
||
function selectedOptionText(select) {
|
||
if (!select || !select.value) return '';
|
||
return select.options[select.selectedIndex]?.textContent?.trim() || select.value;
|
||
}
|
||
|
||
function createFilterChip(label, onClear) {
|
||
const chip = document.createElement('span');
|
||
chip.className = 'ux-filter-chip';
|
||
const text = document.createElement('span');
|
||
text.textContent = label;
|
||
const clear = createButton('×', 'ux-filter-chip-clear', { 'aria-label': `${label} entfernen` });
|
||
clear.addEventListener('click', onClear);
|
||
chip.append(text, clear);
|
||
return chip;
|
||
}
|
||
|
||
function clearControl(control, value = '') {
|
||
if (!control) return;
|
||
control.value = value;
|
||
control.dispatchEvent(new Event(control.tagName === 'INPUT' ? 'input' : 'change', { bubbles: true }));
|
||
queueRefresh();
|
||
}
|
||
|
||
function updateActiveFilters() {
|
||
const container = document.getElementById('ux-active-filters');
|
||
if (!container) return;
|
||
container.replaceChildren();
|
||
|
||
const query = document.getElementById('search-input');
|
||
const platform = document.getElementById('filter-platform');
|
||
const status = document.getElementById('filter-status');
|
||
const period = document.getElementById('history-period');
|
||
const dateFrom = document.getElementById('filter-date-from');
|
||
const dateTo = document.getElementById('filter-date-to');
|
||
const chips = [];
|
||
|
||
if (query?.value.trim()) chips.push(createFilterChip(`Suche: ${query.value.trim()}`, () => clearControl(query)));
|
||
if (platform?.value) chips.push(createFilterChip(`Plattform: ${selectedOptionText(platform)}`, () => clearControl(platform)));
|
||
if (status?.value) chips.push(createFilterChip(`Status: ${selectedOptionText(status)}`, () => clearControl(status)));
|
||
if (period?.value && period.value !== '90' && period.value !== 'custom') {
|
||
chips.push(createFilterChip(`Zeitraum: ${selectedOptionText(period)}`, () => clearControl(period, '90')));
|
||
}
|
||
if (dateFrom?.value) chips.push(createFilterChip(`Von: ${dateFrom.value}`, () => clearControl(dateFrom)));
|
||
if (dateTo?.value) chips.push(createFilterChip(`Bis: ${dateTo.value}`, () => clearControl(dateTo)));
|
||
|
||
if (!chips.length) {
|
||
container.classList.add('is-empty');
|
||
return;
|
||
}
|
||
container.classList.remove('is-empty');
|
||
const label = document.createElement('strong');
|
||
label.textContent = 'Aktive Filter';
|
||
container.append(label, ...chips);
|
||
}
|
||
|
||
function updateResultCount() {
|
||
const target = document.getElementById('ux-article-result-count');
|
||
if (!target) return;
|
||
const info = document.getElementById('history-total-info')?.textContent?.trim() || '';
|
||
const match = info.match(/von\s+(\d+)\s+Eintr/i);
|
||
const visible = document.querySelectorAll('#history-list .history-row').length;
|
||
if (match) target.textContent = `${match[1]} Artikel gefunden`;
|
||
else target.textContent = visible === 1 ? '1 Artikel gefunden' : `${visible} Artikel gefunden`;
|
||
}
|
||
|
||
function syncBatchState() {
|
||
const section = historySection();
|
||
const countText = document.getElementById('history-selected-count')?.textContent || '0';
|
||
const count = Number.parseInt(countText, 10) || 0;
|
||
section?.toggleAttribute('data-has-selection', count > 0);
|
||
const more = section?.querySelector('.ux-batch-more');
|
||
if (!count && more) more.open = false;
|
||
}
|
||
|
||
function refreshPresentation() {
|
||
refreshQueued = false;
|
||
annotateRows();
|
||
syncStatusButtons();
|
||
updateActiveFilters();
|
||
updateResultCount();
|
||
syncBatchState();
|
||
}
|
||
|
||
function queueRefresh() {
|
||
if (refreshQueued) return;
|
||
refreshQueued = true;
|
||
window.requestAnimationFrame(refreshPresentation);
|
||
}
|
||
|
||
function bindFilterState() {
|
||
const ids = ['search-input', 'filter-platform', 'filter-status', 'history-period', 'filter-date-from', 'filter-date-to'];
|
||
for (const id of ids) {
|
||
const control = document.getElementById(id);
|
||
if (!control || control.dataset.uxArticleBound === 'true') continue;
|
||
control.dataset.uxArticleBound = 'true';
|
||
control.addEventListener('input', queueRefresh);
|
||
control.addEventListener('change', queueRefresh);
|
||
}
|
||
document.getElementById('history-reset-filters')?.addEventListener('click', () => window.setTimeout(queueRefresh, 0));
|
||
const search = document.getElementById('search-input');
|
||
if (search) search.placeholder = 'Titel, SKU, Marke oder Beschreibung durchsuchen';
|
||
}
|
||
|
||
function observeArticleOverview() {
|
||
const list = document.getElementById('history-list');
|
||
const shell = document.querySelector('#history .history-table-shell');
|
||
if (list) {
|
||
historyObserver = new MutationObserver(queueRefresh);
|
||
historyObserver.observe(list, { childList: true, subtree: true });
|
||
}
|
||
if (shell) {
|
||
metaObserver = new MutationObserver(queueRefresh);
|
||
metaObserver.observe(shell, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['class'] });
|
||
}
|
||
window.addEventListener('beforeunload', () => {
|
||
historyObserver?.disconnect();
|
||
metaObserver?.disconnect();
|
||
}, { once: true });
|
||
}
|
||
|
||
function initializeArticleOverview() {
|
||
const section = historySection();
|
||
if (!section || section.classList.contains('ux-article-overview-ready')) return;
|
||
createOverviewToolbar();
|
||
enhanceBatchActions();
|
||
bindFilterState();
|
||
applyView(currentView(), { persist: false });
|
||
observeArticleOverview();
|
||
refreshPresentation();
|
||
section.classList.add('ux-article-overview-ready');
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initializeArticleOverview, { once: true });
|
||
} else {
|
||
initializeArticleOverview();
|
||
}
|
||
})(); |