change stage
All checks were successful
Deploy / deploy-production (push) Has been skipped
Deploy / deploy-staging (push) Successful in 11s

This commit is contained in:
2026-06-07 03:14:09 +02:00
parent a408765e76
commit 7020ae0b1c
3 changed files with 74 additions and 2 deletions

View File

@@ -2220,6 +2220,16 @@
value: latest ? fmtNumber(latest.doge_per_day_interval, 4) : 'n/a', value: latest ? fmtNumber(latest.doge_per_day_interval, 4) : 'n/a',
sub: payload?.summary?.current_hashrate_mh ? `Hashrate ${fmtNumber(payload.summary.current_hashrate_mh, 4)} MH/s` : (latest ? `Trend ${latest.trend_label}` : ''), sub: payload?.summary?.current_hashrate_mh ? `Hashrate ${fmtNumber(payload.summary.current_hashrate_mh, 4)} MH/s` : (latest ? `Trend ${latest.trend_label}` : ''),
}), }),
h(StatCard, {
key: 'perday-since-payout',
label: `${currentCoinCurrency} pro Tag seit letzter Auszahlung`,
value: latest && latest.doge_per_day_since_last_payout !== null && latest.doge_per_day_since_last_payout !== undefined
? fmtNumber(latest.doge_per_day_since_last_payout, 4)
: 'n/a',
sub: latest && latest.last_payout_at
? `Seit ${fmtDate(latest.last_payout_at)} · ${fmtNumber(latest.coins_since_last_payout, 6)} ${currentCoinCurrency}`
: 'Noch keine Auszahlung vor dem letzten Upload',
}),
h(StatCard, { h(StatCard, {
key: 'value', key: 'value',
label: 'Aktueller Gegenwert', label: 'Aktueller Gegenwert',
@@ -2290,7 +2300,7 @@
panel('Mining-History', 'Die letzten 10 Mining-Uploads inkl. Performance-Werten und OCR-Metadaten.', h('div', { className: 'mc-table-shell' }, [ 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('table', { key: 'table', className: 'mc-table' }, [
h('thead', { key: 'thead' }, h('tr', null, [ h('thead', { key: 'thead' }, h('tr', null, [
'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Trend', 'Notiz', 'Aktion' 'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Seit Auszahlung/Tag', 'Trend', 'Notiz', 'Aktion'
].map((label) => h('th', { key: label }, label)))), ].map((label) => h('th', { key: label }, label)))),
h('tbody', { key: 'tbody' }, h('tbody', { key: 'tbody' },
measurements.slice(-10).reverse().map((row) => h('tr', { key: row.id }, [ measurements.slice(-10).reverse().map((row) => h('tr', { key: row.id }, [
@@ -2299,6 +2309,9 @@
h('td', { key: 'price' }, row.price_per_coin ? `${fmtNumber(row.price_per_coin, 6)} ${row.price_currency}` : 'n/a'), 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: 'source' }, row.source),
h('td', { key: 'rate' }, fmtNumber(row.doge_per_day_interval, 4)), h('td', { key: 'rate' }, fmtNumber(row.doge_per_day_interval, 4)),
h('td', { key: 'rate-payout' }, row.doge_per_day_since_last_payout !== null && row.doge_per_day_since_last_payout !== undefined
? `${fmtNumber(row.doge_per_day_since_last_payout, 4)}${row.last_payout_at ? ` seit ${fmtDate(row.last_payout_at)}` : ''}`
: 'n/a'),
h('td', { key: 'trend' }, row.trend_label), h('td', { key: 'trend' }, row.trend_label),
h('td', { key: 'note' }, row.note || row.ocr_flags.join(', ') || '—'), h('td', { key: 'note' }, row.note || row.ocr_flags.join(', ') || '—'),
h('td', { key: 'action' }, h('button', { h('td', { key: 'action' }, h('button', {

View File

@@ -47,6 +47,7 @@ final class AnalyticsService
$result = []; $result = [];
$payoutIndex = 0; $payoutIndex = 0;
$payoutsByAsset = []; $payoutsByAsset = [];
$latestPayoutByAsset = [];
$latestPriceByCurrency = []; $latestPriceByCurrency = [];
$lastIndex = count($measurements) - 1; $lastIndex = count($measurements) - 1;
@@ -61,7 +62,13 @@ final class AnalyticsService
} }
$payoutAsset = strtoupper(trim((string) ($payouts[$payoutIndex]['payout_currency'] ?? $coinCurrency))); $payoutAsset = strtoupper(trim((string) ($payouts[$payoutIndex]['payout_currency'] ?? $coinCurrency)));
$payoutsByAsset[$payoutAsset] = ($payoutsByAsset[$payoutAsset] ?? 0.0) + (float) ($payouts[$payoutIndex]['coins_amount'] ?? 0); $payoutAmount = (float) ($payouts[$payoutIndex]['coins_amount'] ?? 0);
$payoutsByAsset[$payoutAsset] = ($payoutsByAsset[$payoutAsset] ?? 0.0) + $payoutAmount;
$latestPayoutByAsset[$payoutAsset] = [
'payout_at' => (string) ($payouts[$payoutIndex]['payout_at'] ?? ''),
'payout_ts' => $payoutTs,
'coins_amount' => $payoutAmount,
];
$payoutIndex++; $payoutIndex++;
} }
@@ -80,6 +87,11 @@ final class AnalyticsService
$perDayInterval = null; $perDayInterval = null;
$perHourPerMhInterval = null; $perHourPerMhInterval = null;
$perDayPerMhInterval = null; $perDayPerMhInterval = null;
$hoursSinceLastPayout = null;
$coinsSinceLastPayout = null;
$perHourSinceLastPayout = null;
$perDaySinceLastPayout = null;
$lastPayoutAt = null;
if (is_array($previous) && $previousMeasuredTs !== null) { if (is_array($previous) && $previousMeasuredTs !== null) {
$intervalStartTs = $previousMeasuredTs; $intervalStartTs = $previousMeasuredTs;
@@ -98,6 +110,18 @@ final class AnalyticsService
} }
} }
if (isset($latestPayoutByAsset[$coinCurrency]) && is_array($latestPayoutByAsset[$coinCurrency])) {
$lastPayout = $latestPayoutByAsset[$coinCurrency];
$lastPayoutTs = (int) ($lastPayout['payout_ts'] ?? 0);
if ($lastPayoutTs > 0 && $measuredTs > $lastPayoutTs) {
$hoursSinceLastPayout = ($measuredTs - $lastPayoutTs) / 3600;
$coinsSinceLastPayout = $visibleCoinsTotal;
$perHourSinceLastPayout = $hoursSinceLastPayout > 0 ? $coinsSinceLastPayout / $hoursSinceLastPayout : null;
$perDaySinceLastPayout = $perHourSinceLastPayout !== null ? $perHourSinceLastPayout * 24 : null;
$lastPayoutAt = (string) ($lastPayout['payout_at'] ?? '');
}
}
$trendLabel = 'stabil'; $trendLabel = 'stabil';
if ($perHourInterval !== null && $previousIntervalRate !== null) { if ($perHourInterval !== null && $previousIntervalRate !== null) {
$delta = $perHourInterval - $previousIntervalRate; $delta = $perHourInterval - $previousIntervalRate;
@@ -188,6 +212,11 @@ final class AnalyticsService
'doge_per_day_interval' => $this->roundOrNull($perDayInterval, 6), 'doge_per_day_interval' => $this->roundOrNull($perDayInterval, 6),
'doge_per_hour_per_mh_interval' => $this->roundOrNull($perHourPerMhInterval, 8), 'doge_per_hour_per_mh_interval' => $this->roundOrNull($perHourPerMhInterval, 8),
'doge_per_day_per_mh_interval' => $this->roundOrNull($perDayPerMhInterval, 8), 'doge_per_day_per_mh_interval' => $this->roundOrNull($perDayPerMhInterval, 8),
'last_payout_at' => $lastPayoutAt,
'hours_since_last_payout' => $this->roundOrNull($hoursSinceLastPayout, 4),
'coins_since_last_payout' => $this->roundOrNull($coinsSinceLastPayout, 6),
'doge_per_hour_since_last_payout' => $this->roundOrNull($perHourSinceLastPayout, 6),
'doge_per_day_since_last_payout' => $this->roundOrNull($perDaySinceLastPayout, 6),
'trend_label' => $trendLabel, 'trend_label' => $trendLabel,
'effective_price_per_coin' => $this->roundOrNull($price, 8), 'effective_price_per_coin' => $this->roundOrNull($price, 8),
'effective_price_currency' => $priceCurrency, 'effective_price_currency' => $priceCurrency,

View File

@@ -21,6 +21,23 @@ $widgetTemplatesById = [];
foreach ($widgetTemplates as $template) { foreach ($widgetTemplates as $template) {
$widgetTemplatesById[(int) ($template['id'] ?? 0)] = $template; $widgetTemplatesById[(int) ($template['id'] ?? 0)] = $template;
} }
$priorityModuleEntries = [];
if ($authUser !== null) {
$accessibleModules = $auth->filterModules(array_values(modules()->all()));
$accessibleModulesByName = [];
foreach ($accessibleModules as $module) {
$name = trim((string) ($module['name'] ?? ''));
if ($name !== '' && !empty($module['enabled'])) {
$accessibleModulesByName[$name] = $module;
}
}
foreach (['mining-checker', 'boersenchecker'] as $moduleName) {
if (isset($accessibleModulesByName[$moduleName])) {
$priorityModuleEntries[] = $accessibleModulesByName[$moduleName];
}
}
}
$GLOBALS['layout_header_base_title'] = 'Nexus'; $GLOBALS['layout_header_base_title'] = 'Nexus';
$GLOBALS['layout_header_title'] = 'Nexus'; $GLOBALS['layout_header_title'] = 'Nexus';
@@ -49,6 +66,19 @@ $renderBookmarks = static function (array $config): array {
}; };
?> ?>
<div class="module-shell"><div class="module-page-bg"><div class="module-page-stack"> <div class="module-shell"><div class="module-page-bg"><div class="module-page-stack">
<?php if ($priorityModuleEntries !== []): ?>
<section class="section-box">
<h2>Schnellzugriff</h2>
<p class="muted">Wichtige Module für deinen direkten Einstieg.</p>
<div class="dashboard-links">
<?php foreach ($priorityModuleEntries as $moduleEntry): ?>
<a class="module-button module-button--secondary module-button--small" href="/module/<?= rawurlencode((string) ($moduleEntry['name'] ?? '')) ?>">
<?= e((string) ($moduleEntry['title'] ?? $moduleEntry['name'] ?? 'Modul')) ?>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php if ($currentDashboard === null): ?> <?php if ($currentDashboard === null): ?>
<section class="section-box"> <section class="section-box">
<h2>Kein Home-Dashboard verfügbar</h2> <h2>Kein Home-Dashboard verfügbar</h2>