From 5c615aa8785b9bf682d46b1a898c8c1708561121 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sun, 21 Jun 2026 02:21:10 +0200 Subject: [PATCH] asdasd --- modules/mining-checker/assets/js/app.js | 116 +++++++++++------- modules/mining-checker/src/Api/Router.php | 10 ++ .../src/Infrastructure/MiningRepository.php | 17 +++ 3 files changed, 101 insertions(+), 42 deletions(-) diff --git a/modules/mining-checker/assets/js/app.js b/modules/mining-checker/assets/js/app.js index 991ecf6f..b02a9f65 100644 --- a/modules/mining-checker/assets/js/app.js +++ b/modules/mining-checker/assets/js/app.js @@ -305,10 +305,10 @@ return 'Historie der letzten Uploads mit Performance- und Trendwerten.'; } if (sectionId === 'wallet') { - return 'Wallet-Snapshots und erkannte Asset-Bestände auswerten.'; + return 'Wallet-Snapshots, Transfers und erkannte Asset-Bestände auswerten.'; } if (sectionId === 'mining') { - return 'Miner, Angebote, Auszahlungen und operative Daten verwalten.'; + return 'Miner, Angebote und operative Daten verwalten.'; } if (sectionId === 'dashboards') { return 'Gespeicherte Auswertungen und Visualisierungen anzeigen.'; @@ -1910,7 +1910,7 @@ }); applySavedPayout(savedPayout); invalidateProjectBootstrapCache(projectKey); - setMessage('Auszahlung gespeichert.'); + setMessage('Wallet-Transfer gespeichert.'); setPayoutForm({ payout_at: '', coins_amount: '', payout_currency: currentSettings.crypto_currency || 'DOGE', note: '' }); setPayoutModalOpen(false); } catch (err) { @@ -1920,6 +1920,32 @@ } } + async function deleteWalletTransfer(transfer) { + if (!transfer || !transfer.id) { + return; + } + + const label = `${fmtNumber(transfer.coins_amount, 6)} ${transfer.payout_currency || ''}`.trim(); + if (!window.confirm(`Diesen Wallet-Transfer (${label}) wirklich loeschen?`)) { + return; + } + + setSaving(true); + setError(''); + setMessage(''); + try { + await request(`${apiBase}/projects/${encodeURIComponent(projectKey)}/payouts/${encodeURIComponent(transfer.id)}`, { + method: 'DELETE', + }); + invalidateProjectBootstrapCache(projectKey); + await reloadBootstrapAfterMutation('Wallet-Transfer geloescht.'); + } catch (err) { + setError(err.message); + } finally { + setSaving(false); + } + } + async function submitMinerOffer(event) { event.preventDefault(); setSaving(true); @@ -2414,13 +2440,13 @@ }), h(StatCard, { key: 'perday-since-payout', - label: `${currentCoinCurrency} pro Tag seit letzter Auszahlung`, + label: `${currentCoinCurrency} pro Tag seit letztem Transfer`, 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', + : 'Noch kein Transfer vor dem letzten Upload', }), h(StatCard, { key: 'value', @@ -2492,7 +2518,7 @@ 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, 'Seit Auszahlung/Tag', 'Trend', 'Notiz', 'Aktion' + 'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Seit Transfer/Tag', '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 }, [ @@ -2652,6 +2678,45 @@ ), ]), ])), + panel('Wallet-Transfers', 'Transfers zum hier verfolgten Wallet erhoehen den Wallet-Bestand und reduzieren den sichtbaren Miner-Bestand. Reinvestierte Krypto-Minerkaeufe werden davon automatisch wieder abgezogen.', [ + h('div', { key: 'actions', className: 'mc-inline-row' }, [ + h('button', { + key: 'add-transfer', + type: 'button', + className: 'mc-button mc-button--secondary', + onClick: () => { + setPayoutForm((previous) => ({ + payout_at: previous.payout_at || nowDateTimeLocalValue(), + coins_amount: previous.coins_amount || '', + payout_currency: currentCoinCurrency, + note: previous.note || '', + })); + setPayoutModalOpen(true); + }, + }, 'Zu Wallet transferieren'), + ]), + h('div', { key: 'transfer-list', className: 'mc-table-shell' }, [ + h('table', { key: 'transfer-table', className: 'mc-table' }, [ + h('thead', { key: 'head' }, h('tr', null, ['Zeit', 'Coins', 'Waehrung', 'Notiz', 'Aktion'].map((label) => h('th', { key: label }, label)))), + h('tbody', { key: 'body' }, + currentPayouts.length + ? currentPayouts.slice().reverse().map((transfer) => h('tr', { key: transfer.id }, [ + h('td', { key: 'time' }, fmtDate(transfer.payout_at)), + h('td', { key: 'coins' }, fmtNumber(transfer.coins_amount, 6)), + h('td', { key: 'currency' }, transfer.payout_currency), + h('td', { key: 'note' }, transfer.note || '—'), + h('td', { key: 'action' }, h('button', { + type: 'button', + className: 'mc-button mc-button--ghost', + onClick: () => deleteWalletTransfer(transfer), + disabled: saving, + }, 'Loeschen')), + ])) + : [h('tr', { key: 'empty' }, h('td', { colSpan: 5 }, 'Noch keine Wallet-Transfers hinterlegt.'))] + ), + ]), + ]), + ]), ]); } @@ -2768,39 +2833,6 @@ ]), ]), ]), - panel('Auszahlungen', 'Auszahlungen reduzieren den sichtbaren Coin-Bestand, bleiben aber in der Gesamtleistung erhalten.', [ - h('div', { key: 'actions', className: 'mc-inline-row' }, [ - h('button', { - key: 'add-payout', - type: 'button', - className: 'mc-button mc-button--secondary', - onClick: () => { - setPayoutForm((previous) => ({ - payout_at: previous.payout_at || nowDateTimeLocalValue(), - coins_amount: previous.coins_amount || '', - payout_currency: currentCoinCurrency, - note: previous.note || '', - })); - setPayoutModalOpen(true); - }, - }, 'Auszahlung erfassen'), - ]), - h('div', { key: 'payout-list', className: 'mc-table-shell' }, [ - h('table', { key: 'payout-table', className: 'mc-table' }, [ - h('thead', { key: 'head' }, h('tr', null, ['Zeit', 'Coins', 'Waehrung', 'Notiz'].map((label) => h('th', { key: label }, label)))), - h('tbody', { key: 'body' }, - currentPayouts.length - ? currentPayouts.slice().reverse().map((payout) => h('tr', { key: payout.id }, [ - h('td', { key: 'time' }, fmtDate(payout.payout_at)), - h('td', { key: 'coins' }, fmtNumber(payout.coins_amount, 6)), - h('td', { key: 'currency' }, payout.payout_currency), - h('td', { key: 'note' }, payout.note || '—'), - ])) - : [h('tr', { key: 'empty' }, h('td', { colSpan: 4 }, 'Noch keine Auszahlungen hinterlegt.'))] - ), - ]), - ]), - ]), panel('Miner-Angebote', currentWalletMiningBalance !== null ? `Hier werden nur Basis-Miner gepflegt. Krypto-Angebote werden gegen deinen aktuellen Wallet-Bestand (${fmtNumber(currentWalletMiningBalance, 8)} ${currentMiningCurrency}) gefiltert.` : 'Hier werden nur Basis-Miner gepflegt. Alle vorgegebenen Geschwindigkeiten und Preise werden daraus automatisch abgeleitet.', @@ -3036,15 +3068,15 @@ ]), ]), ], () => setCostPlanModalOpen(false)) : null, - payoutModalOpen ? renderModal('Auszahlung erfassen', [ + payoutModalOpen ? renderModal('Zu Wallet transferieren', [ h('form', { key: 'form', className: 'mc-form', onSubmit: submitPayout }, [ - inputField('Auszahlungszeitpunkt', 'datetime-local', payoutForm.payout_at, (value) => setPayoutForm({ ...payoutForm, payout_at: value })), + inputField('Transferzeitpunkt', 'datetime-local', payoutForm.payout_at, (value) => setPayoutForm({ ...payoutForm, payout_at: value })), inputField('Coins', 'number', payoutForm.coins_amount, (value) => setPayoutForm({ ...payoutForm, coins_amount: value }), '0.000001'), selectField('Waehrung', payoutForm.payout_currency, [currentCoinCurrency].concat(selectableCurrencies.map((currency) => currency.code).filter((code) => code !== currentCoinCurrency)), (value) => setPayoutForm({ ...payoutForm, payout_currency: value })), textareaField('Notiz', payoutForm.note, (value) => setPayoutForm({ ...payoutForm, note: value })), h('div', { className: 'mc-inline-row' }, [ h('button', { type: 'button', className: 'mc-button mc-button--ghost', onClick: () => setPayoutModalOpen(false) }, 'Abbrechen'), - h('button', { type: 'submit', className: 'mc-button mc-button--secondary', disabled: saving }, saving ? 'Speichert …' : 'Auszahlung speichern'), + h('button', { type: 'submit', className: 'mc-button mc-button--secondary', disabled: saving }, saving ? 'Speichert …' : 'Transfer speichern'), ]), ]), ], () => setPayoutModalOpen(false)) : null, diff --git a/modules/mining-checker/src/Api/Router.php b/modules/mining-checker/src/Api/Router.php index 5e4a3647..12598806 100644 --- a/modules/mining-checker/src/Api/Router.php +++ b/modules/mining-checker/src/Api/Router.php @@ -257,6 +257,11 @@ final class Router Http::json(['data' => $this->savePayout($projectKey, Http::input())], 201); } + if (preg_match('~^payouts/(\d+)$~', $resource, $matches) && $method === 'DELETE') { + $this->deletePayout($projectKey, (int) $matches[1]); + Http::json(['data' => ['deleted' => true]]); + } + if ($resource === 'wallet-snapshots' && $method === 'GET') { Http::json(['data' => $this->walletSnapshots($projectKey)]); } @@ -1766,6 +1771,11 @@ final class Router return $this->repository()->savePayout($projectKey, $payload); } + private function deletePayout(string $projectKey, int $payoutId): void + { + $this->repository()->deletePayout($projectKey, $payoutId); + } + private function walletSnapshots(string $projectKey): array { return $this->repository()->listWalletSnapshots($projectKey, 100); diff --git a/modules/mining-checker/src/Infrastructure/MiningRepository.php b/modules/mining-checker/src/Infrastructure/MiningRepository.php index 78caae71..02507a25 100644 --- a/modules/mining-checker/src/Infrastructure/MiningRepository.php +++ b/modules/mining-checker/src/Infrastructure/MiningRepository.php @@ -442,6 +442,23 @@ final class MiningRepository return $this->normalizeRow($fetch->fetch() ?: []); } + public function deletePayout(string $projectKey, int $payoutId): void + { + if ($payoutId <= 0) { + return; + } + + $stmt = $this->pdo->prepare( + 'DELETE FROM ' . $this->table('payouts') . ' + WHERE id = :id AND project_key = :project_key AND owner_sub = :owner_sub' + ); + $stmt->execute([ + 'id' => $payoutId, + 'project_key' => $projectKey, + 'owner_sub' => $this->ownerSub, + ]); + } + public function listWalletSnapshots(string $projectKey, int $limit = 100): array { $stmt = $this->pdo->prepare(