From de99f2133297df227cd3c4a254303c083ba26961 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sat, 9 May 2026 01:34:41 +0200 Subject: [PATCH] dsasd --- modules/mining-checker/assets/js/app.js | 133 ++++++++++++++++------ modules/mining-checker/design.json | 11 +- modules/mining-checker/src/Api/Router.php | 4 +- 3 files changed, 106 insertions(+), 42 deletions(-) diff --git a/modules/mining-checker/assets/js/app.js b/modules/mining-checker/assets/js/app.js index b940efc..c165605 100644 --- a/modules/mining-checker/assets/js/app.js +++ b/modules/mining-checker/assets/js/app.js @@ -667,9 +667,34 @@ ]); } + function miningCheckerViewFromHref(href) { + try { + const url = new URL(href, window.location.origin); + if (url.pathname !== '/module/mining-checker') { + return null; + } + return url.searchParams.get('view') || 'overview'; + } catch (err) { + return null; + } + } + + function syncMiningCheckerTabButtons(activeTab) { + const buttons = Array.from(document.querySelectorAll('.module-tabs a[href*="/module/mining-checker"]')); + for (const button of buttons) { + const tab = miningCheckerViewFromHref(button.getAttribute('href') || ''); + if (!tab) { + continue; + } + const isActive = tab === activeTab; + button.classList.toggle('module-button--tab-active', isActive); + button.classList.toggle('module-button--tab', !isActive); + } + } + function App() { const [projectKey, setProjectKey] = useState(initialProjectKey); - const [activeTab] = useState(initialActiveTab); + const [activeTab, setActiveTab] = useState(initialActiveTab); const [payload, setPayload] = useState(() => normalizeBootstrap(null, initialProjectKey)); const [dashboardData, setDashboardData] = useState({}); const [loading, setLoading] = useState(true); @@ -1159,14 +1184,46 @@ useEffect(() => { loadBootstrap(projectKey); - }, [projectKey]); + }, [projectKey, activeTab]); useEffect(() => { - if (activeTab === 'settings') { - loadSchemaStatus(projectKey); - loadModuleAuth(); + syncMiningCheckerTabButtons(activeTab); + + function handleNavigationClick(event) { + const link = event.target instanceof Element + ? event.target.closest('.module-tabs a[href*="/module/mining-checker"]') + : null; + if (!link) { + return; + } + + const nextTab = miningCheckerViewFromHref(link.getAttribute('href') || ''); + if (!nextTab || nextTab === activeTab) { + return; + } + + event.preventDefault(); + window.history.pushState({ miningCheckerView: nextTab }, '', `/module/mining-checker?view=${encodeURIComponent(nextTab)}`); + setActiveTab(nextTab); } - }, [activeTab, projectKey]); + + function handlePopState() { + const nextTab = miningCheckerViewFromHref(window.location.href) || 'overview'; + setActiveTab(nextTab); + } + + document.addEventListener('click', handleNavigationClick); + window.addEventListener('popstate', handlePopState); + return () => { + document.removeEventListener('click', handleNavigationClick); + window.removeEventListener('popstate', handlePopState); + }; + }, [activeTab]); + + useEffect(() => { + loadSchemaStatus(projectKey); + loadModuleAuth(); + }, [projectKey]); useEffect(() => { async function loadSavedDashboards() { @@ -1937,8 +1994,8 @@ h('div', { key: 'shell', className: 'mc-shell mc-stack' }, [ error ? h('div', { key: 'error', className: 'mc-alert mc-alert--error' }, error) : null, message ? h('div', { key: 'message', className: 'mc-alert mc-alert--success' }, message) : null, - loading ? h('div', { key: 'loading', className: 'mc-empty' }, 'Lade Mining-Checker Daten …') : null, - payload ? renderTab() : null, + loading ? h('div', { key: 'loading', className: 'mc-alert mc-alert--warning' }, 'Mining-Checker Daten werden aktualisiert …') : null, + renderTab(), ]), ]); @@ -2077,10 +2134,41 @@ } if (activeTab === 'measurements') { + return h('div', { className: 'mc-main-grid' }, [ + h('div', { className: 'mc-stack' }, [ + ]), + panel('Mining-History', 'Die letzten 10 Mining-Uploads inkl. Performance-Werten und OCR-Metadaten.', h('div', { className: 'mc-table-shell' }, [ + h('table', { key: 'table', className: 'mc-table' }, [ + h('thead', { key: 'thead' }, h('tr', null, [ + 'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Trend', 'Notiz', 'Aktion' + ].map((label) => h('th', { key: label }, label)))), + h('tbody', { key: 'tbody' }, + measurements.slice(-10).reverse().map((row) => h('tr', { key: row.id }, [ + h('td', { key: 'measured' }, fmtDate(row.measured_at)), + h('td', { key: 'coins' }, `${fmtNumber(row.coins_total, 6)} ${row.coin_currency || currentCoinCurrency}`), + h('td', { key: 'price' }, row.price_per_coin ? `${fmtNumber(row.price_per_coin, 6)} ${row.price_currency}` : 'n/a'), + h('td', { key: 'source' }, row.source), + h('td', { key: 'rate' }, fmtNumber(row.doge_per_day_interval, 4)), + h('td', { key: 'trend' }, row.trend_label), + h('td', { key: 'note' }, row.note || row.ocr_flags.join(', ') || '—'), + h('td', { key: 'action' }, h('button', { + type: 'button', + className: 'mc-button mc-button--ghost', + onClick: () => deleteMeasurement(row.id), + disabled: saving, + }, 'Loeschen')), + ])) + ), + ]), + ])), + ]); + } + + if (activeTab === 'upload') { return h('div', { className: 'mc-main-grid' }, [ h('div', { className: 'mc-stack' }, [ renderSharedOcrPanel(), - panel('Messpunkt manuell erfassen', 'Direkte Eingabe eines einzelnen Messpunkts mit serverseitiger Validierung.', h('form', { + panel('Mining manuell erfassen', 'Direkte Eingabe eines einzelnen Mining-Messpunkts mit serverseitiger Validierung.', h('form', { className: 'mc-form', onSubmit: function (event) { event.preventDefault(); @@ -2098,7 +2186,7 @@ disabled: saving, }, saving ? 'Speichert …' : 'Messpunkt speichern'), ])), - panel('Import per Copy & Paste', 'Mehrere historische Messpunkte auf einmal einfuegen. Doppelte Eintraege werden ignoriert.', h('form', { + panel('Mining-Import per Copy & Paste', 'Mehrere historische Mining-Messpunkte auf einmal einfuegen. Doppelte Eintraege werden ignoriert.', h('form', { className: 'mc-form', onSubmit: submitMeasurementImport, }, [ @@ -2158,37 +2246,12 @@ ]), ]) : null, ]), - panel('Messhistorie', 'Die letzten 10 Uploads inkl. Performance-Werten und OCR-Metadaten.', h('div', { className: 'mc-table-shell' }, [ - h('table', { key: 'table', className: 'mc-table' }, [ - h('thead', { key: 'thead' }, h('tr', null, [ - 'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Trend', 'Notiz', 'Aktion' - ].map((label) => h('th', { key: label }, label)))), - h('tbody', { key: 'tbody' }, - measurements.slice(-10).reverse().map((row) => h('tr', { key: row.id }, [ - h('td', { key: 'measured' }, fmtDate(row.measured_at)), - h('td', { key: 'coins' }, `${fmtNumber(row.coins_total, 6)} ${row.coin_currency || currentCoinCurrency}`), - h('td', { key: 'price' }, row.price_per_coin ? `${fmtNumber(row.price_per_coin, 6)} ${row.price_currency}` : 'n/a'), - h('td', { key: 'source' }, row.source), - h('td', { key: 'rate' }, fmtNumber(row.doge_per_day_interval, 4)), - h('td', { key: 'trend' }, row.trend_label), - h('td', { key: 'note' }, row.note || row.ocr_flags.join(', ') || '—'), - h('td', { key: 'action' }, h('button', { - type: 'button', - className: 'mc-button mc-button--ghost', - onClick: () => deleteMeasurement(row.id), - disabled: saving, - }, 'Loeschen')), - ])) - ), - ]), - ])), ]); } if (activeTab === 'wallet') { return h('div', { className: 'mc-main-grid' }, [ h('div', { className: 'mc-stack' }, [ - renderSharedOcrPanel(), panel('Wallet-Historie', `Erkannte Wallet-Snapshots. Fuer Reinvestitionen ist aktuell ${currentSettings.crypto_currency || 'DOGE'} entscheidend.`, h('div', { className: 'mc-table-shell' }, [ h('table', { key: 'wallet-table', className: 'mc-table' }, [ h('thead', { key: 'thead' }, h('tr', null, [ diff --git a/modules/mining-checker/design.json b/modules/mining-checker/design.json index 84c28b2..a033573 100644 --- a/modules/mining-checker/design.json +++ b/modules/mining-checker/design.json @@ -3,14 +3,15 @@ "title": "Mining-Checker", "description": "Erfassung, OCR-Auswertung und Analyse von DOGE-Mining-Messwerten als eingebettetes Modul.", "actions": [ + { "label": "Nexus Übersicht", "href": "/modules" }, { "label": "Setup", "href": "/modules/setup/mining-checker", "variant": "secondary" } ], "sections": [ - { "key": "overview", "label": "Ueberblick" }, - { "key": "measurements", "label": "Messpunkte" }, + { "key": "overview", "label": "Übersicht" }, + { "key": "upload", "label": "Upload" }, + { "key": "measurements", "label": "Mining-History" }, { "key": "wallet", "label": "Wallet" }, - { "key": "mining", "label": "Mining" }, - { "key": "dashboards", "label": "Dashboards" }, - { "key": "settings", "label": "Settings" } + { "key": "mining", "label": "Miner-Daten" }, + { "key": "dashboards", "label": "Dashboards" } ] } diff --git a/modules/mining-checker/src/Api/Router.php b/modules/mining-checker/src/Api/Router.php index 3d40e9c..5d95a22 100644 --- a/modules/mining-checker/src/Api/Router.php +++ b/modules/mining-checker/src/Api/Router.php @@ -1224,7 +1224,7 @@ final class Router private function bootstrapMeasurements(string $projectKey, array $settings, string $view): array { - if (in_array($view, ['settings', 'dashboards', 'wallet'], true)) { + if (in_array($view, ['upload', 'dashboards', 'wallet'], true)) { return []; } @@ -1313,7 +1313,7 @@ final class Router private function normalizeBootstrapView(string $view): string { $normalized = trim(strtolower($view)); - return in_array($normalized, ['overview', 'measurements', 'dashboards', 'mining', 'settings'], true) + return in_array($normalized, ['overview', 'upload', 'measurements', 'wallet', 'dashboards', 'mining'], true) ? $normalized : 'overview'; }