dsasd
All checks were successful
Deploy / deploy-staging (push) Successful in 5s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-09 01:34:41 +02:00
parent 693086029d
commit de99f21332
3 changed files with 106 additions and 42 deletions

View File

@@ -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() { function App() {
const [projectKey, setProjectKey] = useState(initialProjectKey); const [projectKey, setProjectKey] = useState(initialProjectKey);
const [activeTab] = useState(initialActiveTab); const [activeTab, setActiveTab] = useState(initialActiveTab);
const [payload, setPayload] = useState(() => normalizeBootstrap(null, initialProjectKey)); const [payload, setPayload] = useState(() => normalizeBootstrap(null, initialProjectKey));
const [dashboardData, setDashboardData] = useState({}); const [dashboardData, setDashboardData] = useState({});
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -1159,14 +1184,46 @@
useEffect(() => { useEffect(() => {
loadBootstrap(projectKey); loadBootstrap(projectKey);
}, [projectKey]); }, [projectKey, activeTab]);
useEffect(() => {
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);
}
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(() => { useEffect(() => {
if (activeTab === 'settings') {
loadSchemaStatus(projectKey); loadSchemaStatus(projectKey);
loadModuleAuth(); loadModuleAuth();
} }, [projectKey]);
}, [activeTab, projectKey]);
useEffect(() => { useEffect(() => {
async function loadSavedDashboards() { async function loadSavedDashboards() {
@@ -1937,8 +1994,8 @@
h('div', { key: 'shell', className: 'mc-shell mc-stack' }, [ h('div', { key: 'shell', className: 'mc-shell mc-stack' }, [
error ? h('div', { key: 'error', className: 'mc-alert mc-alert--error' }, error) : null, 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, 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, loading ? h('div', { key: 'loading', className: 'mc-alert mc-alert--warning' }, 'Mining-Checker Daten werden aktualisiert …') : null,
payload ? renderTab() : null, renderTab(),
]), ]),
]); ]);
@@ -2077,10 +2134,41 @@
} }
if (activeTab === 'measurements') { 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' }, [ return h('div', { className: 'mc-main-grid' }, [
h('div', { className: 'mc-stack' }, [ h('div', { className: 'mc-stack' }, [
renderSharedOcrPanel(), 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', className: 'mc-form',
onSubmit: function (event) { onSubmit: function (event) {
event.preventDefault(); event.preventDefault();
@@ -2098,7 +2186,7 @@
disabled: saving, disabled: saving,
}, saving ? 'Speichert …' : 'Messpunkt speichern'), }, 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', className: 'mc-form',
onSubmit: submitMeasurementImport, onSubmit: submitMeasurementImport,
}, [ }, [
@@ -2158,37 +2246,12 @@
]), ]),
]) : null, ]) : 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') { if (activeTab === 'wallet') {
return h('div', { className: 'mc-main-grid' }, [ return h('div', { className: 'mc-main-grid' }, [
h('div', { className: 'mc-stack' }, [ 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' }, [ 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('table', { key: 'wallet-table', className: 'mc-table' }, [
h('thead', { key: 'thead' }, h('tr', null, [ h('thead', { key: 'thead' }, h('tr', null, [

View File

@@ -3,14 +3,15 @@
"title": "Mining-Checker", "title": "Mining-Checker",
"description": "Erfassung, OCR-Auswertung und Analyse von DOGE-Mining-Messwerten als eingebettetes Modul.", "description": "Erfassung, OCR-Auswertung und Analyse von DOGE-Mining-Messwerten als eingebettetes Modul.",
"actions": [ "actions": [
{ "label": "Nexus Übersicht", "href": "/modules" },
{ "label": "Setup", "href": "/modules/setup/mining-checker", "variant": "secondary" } { "label": "Setup", "href": "/modules/setup/mining-checker", "variant": "secondary" }
], ],
"sections": [ "sections": [
{ "key": "overview", "label": "Ueberblick" }, { "key": "overview", "label": "Übersicht" },
{ "key": "measurements", "label": "Messpunkte" }, { "key": "upload", "label": "Upload" },
{ "key": "measurements", "label": "Mining-History" },
{ "key": "wallet", "label": "Wallet" }, { "key": "wallet", "label": "Wallet" },
{ "key": "mining", "label": "Mining" }, { "key": "mining", "label": "Miner-Daten" },
{ "key": "dashboards", "label": "Dashboards" }, { "key": "dashboards", "label": "Dashboards" }
{ "key": "settings", "label": "Settings" }
] ]
} }

View File

@@ -1224,7 +1224,7 @@ final class Router
private function bootstrapMeasurements(string $projectKey, array $settings, string $view): array 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 []; return [];
} }
@@ -1313,7 +1313,7 @@ final class Router
private function normalizeBootstrapView(string $view): string private function normalizeBootstrapView(string $view): string
{ {
$normalized = trim(strtolower($view)); $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 ? $normalized
: 'overview'; : 'overview';
} }