asdasd
This commit is contained in:
@@ -305,10 +305,10 @@
|
|||||||
return 'Historie der letzten Uploads mit Performance- und Trendwerten.';
|
return 'Historie der letzten Uploads mit Performance- und Trendwerten.';
|
||||||
}
|
}
|
||||||
if (sectionId === 'wallet') {
|
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') {
|
if (sectionId === 'mining') {
|
||||||
return 'Miner, Angebote, Auszahlungen und operative Daten verwalten.';
|
return 'Miner, Angebote und operative Daten verwalten.';
|
||||||
}
|
}
|
||||||
if (sectionId === 'dashboards') {
|
if (sectionId === 'dashboards') {
|
||||||
return 'Gespeicherte Auswertungen und Visualisierungen anzeigen.';
|
return 'Gespeicherte Auswertungen und Visualisierungen anzeigen.';
|
||||||
@@ -1910,7 +1910,7 @@
|
|||||||
});
|
});
|
||||||
applySavedPayout(savedPayout);
|
applySavedPayout(savedPayout);
|
||||||
invalidateProjectBootstrapCache(projectKey);
|
invalidateProjectBootstrapCache(projectKey);
|
||||||
setMessage('Auszahlung gespeichert.');
|
setMessage('Wallet-Transfer gespeichert.');
|
||||||
setPayoutForm({ payout_at: '', coins_amount: '', payout_currency: currentSettings.crypto_currency || 'DOGE', note: '' });
|
setPayoutForm({ payout_at: '', coins_amount: '', payout_currency: currentSettings.crypto_currency || 'DOGE', note: '' });
|
||||||
setPayoutModalOpen(false);
|
setPayoutModalOpen(false);
|
||||||
} catch (err) {
|
} 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) {
|
async function submitMinerOffer(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
@@ -2414,13 +2440,13 @@
|
|||||||
}),
|
}),
|
||||||
h(StatCard, {
|
h(StatCard, {
|
||||||
key: 'perday-since-payout',
|
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
|
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)
|
? fmtNumber(latest.doge_per_day_since_last_payout, 4)
|
||||||
: 'n/a',
|
: 'n/a',
|
||||||
sub: latest && latest.last_payout_at
|
sub: latest && latest.last_payout_at
|
||||||
? `Seit ${fmtDate(latest.last_payout_at)} · ${fmtNumber(latest.coins_since_last_payout, 6)} ${currentCoinCurrency}`
|
? `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, {
|
h(StatCard, {
|
||||||
key: 'value',
|
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' }, [
|
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, 'Seit Auszahlung/Tag', 'Trend', 'Notiz', 'Aktion'
|
'Zeit', 'Coins', 'Kurs', 'Quelle', perDayLabel, 'Seit Transfer/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 }, [
|
||||||
@@ -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
|
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. 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.',
|
: 'Hier werden nur Basis-Miner gepflegt. Alle vorgegebenen Geschwindigkeiten und Preise werden daraus automatisch abgeleitet.',
|
||||||
@@ -3036,15 +3068,15 @@
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
], () => setCostPlanModalOpen(false)) : null,
|
], () => setCostPlanModalOpen(false)) : null,
|
||||||
payoutModalOpen ? renderModal('Auszahlung erfassen', [
|
payoutModalOpen ? renderModal('Zu Wallet transferieren', [
|
||||||
h('form', { key: 'form', className: 'mc-form', onSubmit: submitPayout }, [
|
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'),
|
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 })),
|
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 })),
|
textareaField('Notiz', payoutForm.note, (value) => setPayoutForm({ ...payoutForm, note: value })),
|
||||||
h('div', { className: 'mc-inline-row' }, [
|
h('div', { className: 'mc-inline-row' }, [
|
||||||
h('button', { type: 'button', className: 'mc-button mc-button--ghost', onClick: () => setPayoutModalOpen(false) }, 'Abbrechen'),
|
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,
|
], () => setPayoutModalOpen(false)) : null,
|
||||||
|
|||||||
@@ -257,6 +257,11 @@ final class Router
|
|||||||
Http::json(['data' => $this->savePayout($projectKey, Http::input())], 201);
|
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') {
|
if ($resource === 'wallet-snapshots' && $method === 'GET') {
|
||||||
Http::json(['data' => $this->walletSnapshots($projectKey)]);
|
Http::json(['data' => $this->walletSnapshots($projectKey)]);
|
||||||
}
|
}
|
||||||
@@ -1766,6 +1771,11 @@ final class Router
|
|||||||
return $this->repository()->savePayout($projectKey, $payload);
|
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
|
private function walletSnapshots(string $projectKey): array
|
||||||
{
|
{
|
||||||
return $this->repository()->listWalletSnapshots($projectKey, 100);
|
return $this->repository()->listWalletSnapshots($projectKey, 100);
|
||||||
|
|||||||
@@ -442,6 +442,23 @@ final class MiningRepository
|
|||||||
return $this->normalizeRow($fetch->fetch() ?: []);
|
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
|
public function listWalletSnapshots(string $projectKey, int $limit = 100): array
|
||||||
{
|
{
|
||||||
$stmt = $this->pdo->prepare(
|
$stmt = $this->pdo->prepare(
|
||||||
|
|||||||
Reference in New Issue
Block a user