diff --git a/public/ux-foundation.js b/public/ux-foundation.js new file mode 100644 index 0000000..35c9072 --- /dev/null +++ b/public/ux-foundation.js @@ -0,0 +1,246 @@ +(() => { + 'use strict'; + + const NAV_SECTIONS = [ + { + id: 'start', + label: 'Start', + tabs: ['dashboard', 'today', 'generator'], + }, + { + id: 'commerce', + label: 'Verkaufen', + tabs: ['history', 'quality-center', 'publish', 'inventory'], + }, + { + id: 'content', + label: 'Inhalte & AI', + tabs: ['media-library', 'image-factory', 'flux-studio', 'templates'], + }, + { + id: 'system', + label: 'Werkzeuge & System', + collapsible: true, + tabs: ['extensions', 'settings', 'admin', 'fees', 'trash'], + }, + ]; + + const VIEW_META = { + dashboard: { title: 'Übersicht', kicker: 'Start' }, + today: { title: 'Aufgaben', kicker: 'Arbeitsbereich' }, + generator: { title: 'Neuer Artikel', kicker: 'Erstellen' }, + history: { title: 'Artikel', kicker: 'Verkaufen' }, + 'quality-center': { title: 'Qualität', kicker: 'Optimieren' }, + publish: { title: 'Veröffentlichen', kicker: 'Marktplätze' }, + inventory: { title: 'Lager', kicker: 'Bestand' }, + templates: { title: 'Vorlagen', kicker: 'Inhalte' }, + 'flux-studio': { title: 'FLUX Studio', kicker: 'AI-Bilder' }, + 'media-library': { title: 'Medien', kicker: 'Bilder' }, + 'image-factory': { title: 'Bildwerkstatt', kicker: 'Produktion' }, + extensions: { title: 'Erweiterungen', kicker: 'Browser' }, + settings: { title: 'Einstellungen', kicker: 'System' }, + admin: { title: 'Verwaltung', kicker: 'Benutzer & Betrieb' }, + fees: { title: 'Gebühren', kicker: 'Kalkulation' }, + trash: { title: 'Papierkorb', kicker: 'Wiederherstellen' }, + }; + + const NAV_LABELS = { + dashboard: 'Übersicht', + today: 'Aufgaben', + generator: 'Neuer Artikel', + history: 'Artikel', + 'quality-center': 'Qualität', + publish: 'Veröffentlichen', + inventory: 'Lager', + templates: 'Vorlagen', + 'flux-studio': 'FLUX Studio', + 'media-library': 'Medien', + 'image-factory': 'Bildwerkstatt', + extensions: 'Erweiterungen', + settings: 'Einstellungen', + admin: 'Verwaltung', + fees: 'Gebühren', + trash: 'Papierkorb', + }; + + function getNavItem(tab) { + return document.querySelector(`.sidebar-nav .nav-item[data-tab="${tab}"]`); + } + + function createSection(section) { + const element = document.createElement(section.collapsible ? 'details' : 'section'); + element.className = `ux-nav-section ux-nav-section-${section.id}`; + element.dataset.uxNavSection = section.id; + + if (section.collapsible) { + const stored = localStorage.getItem('vendoo_ux_system_nav_open'); + element.open = stored === 'true' || section.tabs.some(tab => getNavItem(tab)?.classList.contains('active')); + const summary = document.createElement('summary'); + summary.textContent = section.label; + element.append(summary); + element.addEventListener('toggle', () => { + localStorage.setItem('vendoo_ux_system_nav_open', String(element.open)); + }); + } else { + const label = document.createElement('div'); + label.className = 'ux-nav-section-label'; + label.textContent = section.label; + element.append(label); + } + + const items = document.createElement('div'); + items.className = 'ux-nav-section-items'; + items.dataset.uxNavItems = section.id; + element.append(items); + return element; + } + + function reorganizeNavigation() { + const nav = document.querySelector('.sidebar-nav'); + if (!nav || nav.querySelector('.ux-nav-sections')) return; + + const itemsByTab = new Map(); + nav.querySelectorAll('.nav-item[data-tab]').forEach(item => { + itemsByTab.set(item.dataset.tab, item); + const label = item.querySelector('span'); + if (label && NAV_LABELS[item.dataset.tab]) label.textContent = NAV_LABELS[item.dataset.tab]; + item.setAttribute('aria-current', item.classList.contains('active') ? 'page' : 'false'); + }); + + const shell = document.createElement('div'); + shell.className = 'ux-nav-sections'; + + for (const section of NAV_SECTIONS) { + const sectionElement = createSection(section); + const container = sectionElement.querySelector('.ux-nav-section-items'); + for (const tab of section.tabs) { + const item = itemsByTab.get(tab); + if (item) container.append(item); + } + shell.append(sectionElement); + } + + const assigned = new Set(NAV_SECTIONS.flatMap(section => section.tabs)); + const remaining = [...itemsByTab.entries()].filter(([tab]) => !assigned.has(tab)); + if (remaining.length) { + const other = createSection({ id: 'other', label: 'Weitere', collapsible: true, tabs: [] }); + const container = other.querySelector('.ux-nav-section-items'); + remaining.forEach(([, item]) => container.append(item)); + shell.append(other); + } + + nav.querySelectorAll(':scope > .nav-group').forEach(group => group.remove()); + nav.append(shell); + } + + function activateView(tab) { + const item = getNavItem(tab); + if (!item || item.classList.contains('hidden')) return; + const systemSection = item.closest('details.ux-nav-section'); + if (systemSection) systemSection.open = true; + item.click(); + } + + function createDashboardCommandCenter() { + const dashboard = document.getElementById('dashboard'); + const layout = dashboard?.querySelector('.dashboard-layout'); + if (!dashboard || !layout || dashboard.querySelector('.ux-command-center')) return; + + const panel = document.createElement('section'); + panel.className = 'ux-command-center'; + panel.setAttribute('aria-label', 'Schnellstart'); + panel.innerHTML = ` +
Die wichtigsten Wege sind direkt erreichbar. Detailauswertungen bleiben darunter verfügbar, ohne den Einstieg zu überladen.
+