dsasd
This commit is contained in:
@@ -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(() => {
|
useEffect(() => {
|
||||||
if (activeTab === 'settings') {
|
syncMiningCheckerTabButtons(activeTab);
|
||||||
loadSchemaStatus(projectKey);
|
|
||||||
loadModuleAuth();
|
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(() => {
|
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, [
|
||||||
|
|||||||
@@ -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" }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user