adasd
This commit is contained in:
@@ -136,6 +136,37 @@
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-card {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-balance {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: var(--mc-text);
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-row {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-asset-row strong {
|
||||
color: var(--mc-text);
|
||||
}
|
||||
|
||||
#mining-checker-app .mc-main-grid {
|
||||
grid-template-columns: minmax(300px, 380px) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
@@ -2134,9 +2134,7 @@
|
||||
}
|
||||
|
||||
if (activeTab === 'measurements') {
|
||||
return h('div', { className: 'mc-main-grid' }, [
|
||||
h('div', { className: 'mc-stack' }, [
|
||||
]),
|
||||
return 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, [
|
||||
@@ -2165,8 +2163,8 @@
|
||||
}
|
||||
|
||||
if (activeTab === 'upload') {
|
||||
return h('div', { className: 'mc-main-grid' }, [
|
||||
h('div', { className: 'mc-stack' }, [
|
||||
return h('div', { className: 'mc-stack' }, [
|
||||
h('div', { className: 'mc-two-col' }, [
|
||||
renderSharedOcrPanel(),
|
||||
panel('Mining manuell erfassen', 'Direkte Eingabe eines einzelnen Mining-Messpunkts mit serverseitiger Validierung.', h('form', {
|
||||
className: 'mc-form',
|
||||
@@ -2186,28 +2184,6 @@
|
||||
disabled: saving,
|
||||
}, saving ? 'Speichert …' : 'Messpunkt speichern'),
|
||||
])),
|
||||
panel('Mining-Import per Copy & Paste', 'Mehrere historische Mining-Messpunkte auf einmal einfuegen. Doppelte Eintraege werden ignoriert.', h('form', {
|
||||
className: 'mc-form',
|
||||
onSubmit: submitMeasurementImport,
|
||||
}, [
|
||||
h('div', { className: 'mc-inline-row' }, [
|
||||
h('button', {
|
||||
key: 'help',
|
||||
type: 'button',
|
||||
className: 'mc-button mc-button--ghost',
|
||||
onClick: () => setImportHelpOpen(true),
|
||||
}, 'Import-Hilfe'),
|
||||
]),
|
||||
displayField('Format', 'DD.MM.YYYY HH:MM | Coins | Kurs | Waehrung | Notiz'),
|
||||
textareaField('Importdaten', importForm.rows_text, (value) => setImportForm({ ...importForm, rows_text: value })),
|
||||
selectField('Standard-Waehrung', importForm.default_currency, [''].concat(selectableCurrencies.map((currency) => currency.code)), (value) => setImportForm({ ...importForm, default_currency: value })),
|
||||
selectField('Import-Quelle', importForm.source, ['manual', 'seed_import'], (value) => setImportForm({ ...importForm, source: value })),
|
||||
h('button', {
|
||||
type: 'submit',
|
||||
className: 'mc-button mc-button--secondary',
|
||||
disabled: saving,
|
||||
}, saving ? 'Importiert …' : 'Import ausfuehren'),
|
||||
])),
|
||||
importHelpOpen ? h('div', { className: 'mc-modal-backdrop', onClick: () => setImportHelpOpen(false) }, [
|
||||
h('div', {
|
||||
key: 'modal',
|
||||
@@ -2246,16 +2222,56 @@
|
||||
]),
|
||||
]) : null,
|
||||
]),
|
||||
panel('Mining-Import per Copy & Paste', 'Mehrere historische Mining-Messpunkte auf einmal einfuegen. Doppelte Eintraege werden ignoriert.', h('form', {
|
||||
className: 'mc-form',
|
||||
onSubmit: submitMeasurementImport,
|
||||
}, [
|
||||
h('div', { className: 'mc-inline-row' }, [
|
||||
h('button', {
|
||||
key: 'help',
|
||||
type: 'button',
|
||||
className: 'mc-button mc-button--ghost',
|
||||
onClick: () => setImportHelpOpen(true),
|
||||
}, 'Import-Hilfe'),
|
||||
]),
|
||||
displayField('Format', 'DD.MM.YYYY HH:MM | Coins | Kurs | Waehrung | Notiz'),
|
||||
textareaField('Importdaten', importForm.rows_text, (value) => setImportForm({ ...importForm, rows_text: value })),
|
||||
selectField('Standard-Waehrung', importForm.default_currency, [''].concat(selectableCurrencies.map((currency) => currency.code)), (value) => setImportForm({ ...importForm, default_currency: value })),
|
||||
selectField('Import-Quelle', importForm.source, ['manual', 'seed_import'], (value) => setImportForm({ ...importForm, source: value })),
|
||||
h('button', {
|
||||
type: 'submit',
|
||||
className: 'mc-button mc-button--secondary',
|
||||
disabled: saving,
|
||||
}, saving ? 'Importiert …' : 'Import ausfuehren'),
|
||||
])),
|
||||
]);
|
||||
}
|
||||
|
||||
if (activeTab === 'wallet') {
|
||||
return h('div', { className: 'mc-main-grid' }, [
|
||||
h('div', { className: 'mc-stack' }, [
|
||||
panel('Wallet-Historie', `Erkannte Wallet-Snapshots. Fuer Reinvestitionen ist aktuell ${currentSettings.crypto_currency || 'DOGE'} entscheidend.`, h('div', { className: 'mc-table-shell' }, [
|
||||
const latestWalletSnapshot = currentWalletSnapshots.length ? currentWalletSnapshots[0] : null;
|
||||
const latestWalletAssets = latestWalletSnapshot && latestWalletSnapshot.balances_json && typeof latestWalletSnapshot.balances_json === 'object'
|
||||
? Object.entries(latestWalletSnapshot.balances_json)
|
||||
: [];
|
||||
|
||||
return h('div', { className: 'mc-stack' }, [
|
||||
panel('Wallet-Bestaende', 'Der letzte Wallet-Snapshot zeigt alle erkannten Assets separat.', latestWalletAssets.length
|
||||
? h('div', { className: 'mc-asset-grid' }, latestWalletAssets.map(([code, asset]) => {
|
||||
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
||||
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
||||
const priceCurrency = asset && typeof asset === 'object' ? asset.price_currency : null;
|
||||
return h('div', { key: code, className: 'mc-display-field mc-asset-card' }, [
|
||||
h('div', { key: 'code', className: 'mc-field-label' }, code),
|
||||
h('div', { key: 'balance', className: 'mc-asset-balance' }, `${fmtNumber(balance, 8)} ${code}`),
|
||||
h('div', { key: 'price', className: 'mc-text' }, priceAmount !== null && priceAmount !== undefined
|
||||
? `1 ${code} = ${fmtNumber(priceAmount, 6)} ${priceCurrency || ''}`.trim()
|
||||
: 'Kein Screenshot-Kurs erkannt'),
|
||||
]);
|
||||
}))
|
||||
: h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')),
|
||||
panel('Wallet-Historie', 'Erkannte Wallet-Snapshots mit allen aus dem Screenshot gelesenen Assets.', h('div', { className: 'mc-table-shell' }, [
|
||||
h('table', { key: 'wallet-table', className: 'mc-table' }, [
|
||||
h('thead', { key: 'thead' }, h('tr', null, [
|
||||
'Zeit', 'Mining-Waehrung', 'Bestand', 'Gesamtwert', 'Quelle', 'Assets'
|
||||
'Zeit', 'Mining-Waehrung', 'Mining-Bestand', 'Quelle', 'Assets'
|
||||
].map((label) => h('th', { key: label }, label)))),
|
||||
h('tbody', { key: 'tbody' },
|
||||
currentWalletSnapshots.length
|
||||
@@ -2263,20 +2279,24 @@
|
||||
h('td', { key: 'measured' }, fmtDate(row.measured_at)),
|
||||
h('td', { key: 'currency' }, row.wallet_currency || currentSettings.crypto_currency || 'DOGE'),
|
||||
h('td', { key: 'balance' }, row.wallet_balance !== null && row.wallet_balance !== undefined ? `${fmtNumber(row.wallet_balance, 8)} ${row.wallet_currency || ''}`.trim() : 'n/a'),
|
||||
h('td', { key: 'value' }, row.total_value_amount !== null && row.total_value_amount !== undefined ? `${fmtNumber(row.total_value_amount, 4)} ${row.total_value_currency || ''}`.trim() : 'n/a'),
|
||||
h('td', { key: 'source' }, row.source || 'manual'),
|
||||
h('td', { key: 'assets' }, Object.entries(row.balances_json || {}).slice(0, 6).map(([code, asset]) => {
|
||||
h('td', { key: 'assets' }, h('div', { className: 'mc-asset-list' }, Object.entries(row.balances_json || {}).map(([code, asset]) => {
|
||||
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
||||
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
||||
const priceCurrency = asset && typeof asset === 'object' ? asset.price_currency : null;
|
||||
return `${fmtNumber(balance, 8)} ${code}${priceAmount ? ` @ ${fmtNumber(priceAmount, 6)} ${priceCurrency || ''}`.trim() : ''}`;
|
||||
}).join(' · ') || 'n/a'),
|
||||
return h('div', { key: code, className: 'mc-asset-row' }, [
|
||||
h('strong', { key: 'code' }, code),
|
||||
h('span', { key: 'balance' }, `${fmtNumber(balance, 8)} ${code}`),
|
||||
priceAmount !== null && priceAmount !== undefined
|
||||
? h('span', { key: 'price', className: 'mc-text' }, `1 ${code} = ${fmtNumber(priceAmount, 6)} ${priceCurrency || ''}`.trim())
|
||||
: null,
|
||||
]);
|
||||
}))),
|
||||
]))
|
||||
: [h('tr', { key: 'empty' }, h('td', { colSpan: 6 }, 'Noch keine Wallet-Snapshots gespeichert.'))]
|
||||
: [h('tr', { key: 'empty' }, h('td', { colSpan: 5 }, 'Noch keine Wallet-Snapshots gespeichert.'))]
|
||||
),
|
||||
]),
|
||||
])),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Modules\MiningChecker\Infrastructure\ConnectionFactory;
|
||||
use Modules\MiningChecker\Infrastructure\MiningRepository;
|
||||
use Modules\MiningChecker\Infrastructure\ModuleConfig;
|
||||
use Modules\MiningChecker\Infrastructure\SchemaManager;
|
||||
|
||||
spl_autoload_register(static function (string $class): void {
|
||||
$prefix = 'Modules\\MiningChecker\\';
|
||||
@@ -19,6 +21,144 @@ spl_autoload_register(static function (string $class): void {
|
||||
});
|
||||
|
||||
$mm = isset($modules) && $modules instanceof App\ModuleManager ? $modules : modules();
|
||||
$moduleName = 'mining-checker';
|
||||
|
||||
$mm->registerFunction($moduleName, 'runtime_settings', static function (): array {
|
||||
$moduleBasePath = __DIR__;
|
||||
$config = ModuleConfig::load($moduleBasePath);
|
||||
$projectKey = $config->defaultProjectKey();
|
||||
$user = auth_user() ?? [];
|
||||
$ownerSub = trim((string) ($user['sub'] ?? '')) !== '' ? trim((string) ($user['sub'] ?? '')) : 'local';
|
||||
|
||||
$pdo = ConnectionFactory::make($config);
|
||||
$repository = new MiningRepository($pdo, $config->tablePrefix(), null, $ownerSub);
|
||||
|
||||
$settings = $repository->getSettings($projectKey) ?? [];
|
||||
$displayTimezone = trim((string) ($settings['display_timezone'] ?? 'Europe/Berlin'));
|
||||
if ($displayTimezone === '') {
|
||||
$displayTimezone = 'Europe/Berlin';
|
||||
}
|
||||
|
||||
$baselineMeasuredAt = trim((string) ($settings['baseline_measured_at'] ?? ''));
|
||||
if ($baselineMeasuredAt !== '') {
|
||||
try {
|
||||
$baselineMeasuredAt = (new DateTimeImmutable($baselineMeasuredAt, new DateTimeZone('UTC')))
|
||||
->setTimezone(new DateTimeZone($displayTimezone))
|
||||
->format('Y-m-d\TH:i');
|
||||
} catch (\Throwable) {
|
||||
$baselineMeasuredAt = '';
|
||||
}
|
||||
}
|
||||
|
||||
$preferredCurrencies = $settings['preferred_currencies'] ?? ['DOGE', 'USD', 'EUR'];
|
||||
if (is_string($preferredCurrencies)) {
|
||||
$preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: [];
|
||||
}
|
||||
$preferredCurrencies = array_values(array_filter(array_map(
|
||||
static fn (mixed $value): string => strtoupper(trim((string) $value)),
|
||||
is_array($preferredCurrencies) ? $preferredCurrencies : []
|
||||
), static fn (string $value): bool => $value !== ''));
|
||||
|
||||
return [
|
||||
'baseline_measured_at' => $baselineMeasuredAt,
|
||||
'baseline_coins_total' => isset($settings['baseline_coins_total']) ? (string) $settings['baseline_coins_total'] : '',
|
||||
'daily_cost_amount' => isset($settings['daily_cost_amount']) ? (string) $settings['daily_cost_amount'] : '',
|
||||
'daily_cost_currency' => strtoupper(trim((string) ($settings['daily_cost_currency'] ?? 'EUR'))) ?: 'EUR',
|
||||
'report_currency' => strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR',
|
||||
'crypto_currency' => strtoupper(trim((string) ($settings['crypto_currency'] ?? 'DOGE'))) ?: 'DOGE',
|
||||
'display_timezone' => $displayTimezone,
|
||||
'preferred_currencies' => $preferredCurrencies,
|
||||
];
|
||||
});
|
||||
|
||||
$mm->registerFunction($moduleName, 'save_runtime_settings', static function (array $payload): array {
|
||||
$moduleBasePath = __DIR__;
|
||||
$config = ModuleConfig::load($moduleBasePath);
|
||||
$projectKey = $config->defaultProjectKey();
|
||||
$user = auth_user() ?? [];
|
||||
$ownerSub = trim((string) ($user['sub'] ?? '')) !== '' ? trim((string) ($user['sub'] ?? '')) : 'local';
|
||||
|
||||
$pdo = ConnectionFactory::make($config);
|
||||
$schema = new SchemaManager($pdo, $config->tablePrefix(), $moduleBasePath);
|
||||
$schema->ensureSchema();
|
||||
|
||||
$repository = new MiningRepository($pdo, $config->tablePrefix(), null, $ownerSub);
|
||||
$repository->ensureProject($projectKey, strtoupper(str_replace('-', ' ', $projectKey)));
|
||||
|
||||
$existing = $repository->getSettings($projectKey) ?? [];
|
||||
$displayTimezone = trim((string) ($payload['display_timezone'] ?? ($existing['display_timezone'] ?? 'Europe/Berlin')));
|
||||
if ($displayTimezone === '') {
|
||||
$displayTimezone = 'Europe/Berlin';
|
||||
}
|
||||
|
||||
try {
|
||||
$displayTz = new DateTimeZone($displayTimezone);
|
||||
} catch (\Throwable) {
|
||||
throw new RuntimeException('Ungueltige Anzeige-Zeitzone.');
|
||||
}
|
||||
|
||||
$baselineInput = trim((string) ($payload['baseline_measured_at'] ?? ($existing['baseline_measured_at'] ?? '')));
|
||||
if ($baselineInput === '') {
|
||||
throw new RuntimeException('Baseline Zeitpunkt fehlt.');
|
||||
}
|
||||
|
||||
try {
|
||||
$baselineUtc = (new DateTimeImmutable($baselineInput, $displayTz))
|
||||
->setTimezone(new DateTimeZone('UTC'))
|
||||
->format('Y-m-d H:i:s');
|
||||
} catch (\Throwable) {
|
||||
throw new RuntimeException('Baseline Zeitpunkt ist ungueltig.');
|
||||
}
|
||||
|
||||
$baselineCoins = trim((string) ($payload['baseline_coins_total'] ?? ($existing['baseline_coins_total'] ?? '')));
|
||||
$dailyCostAmount = trim((string) ($payload['daily_cost_amount'] ?? ($existing['daily_cost_amount'] ?? '')));
|
||||
if ($baselineCoins === '' || !is_numeric($baselineCoins)) {
|
||||
throw new RuntimeException('Baseline Coins muessen numerisch sein.');
|
||||
}
|
||||
if ($dailyCostAmount === '' || !is_numeric($dailyCostAmount)) {
|
||||
throw new RuntimeException('Taegliche Kosten muessen numerisch sein.');
|
||||
}
|
||||
|
||||
$preferredCurrencies = $payload['preferred_currencies'] ?? ($existing['preferred_currencies'] ?? ['DOGE', 'USD', 'EUR']);
|
||||
if (is_string($preferredCurrencies)) {
|
||||
$preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: [];
|
||||
}
|
||||
$preferredCurrencies = array_values(array_unique(array_filter(array_map(
|
||||
static fn (mixed $value): string => strtoupper(trim((string) $value)),
|
||||
is_array($preferredCurrencies) ? $preferredCurrencies : []
|
||||
), static fn (string $value): bool => $value !== '')));
|
||||
|
||||
$settings = [
|
||||
'baseline_measured_at' => $baselineUtc,
|
||||
'baseline_coins_total' => (float) $baselineCoins,
|
||||
'daily_cost_amount' => (float) $dailyCostAmount,
|
||||
'daily_cost_currency' => strtoupper(trim((string) ($payload['daily_cost_currency'] ?? ($existing['daily_cost_currency'] ?? 'EUR')))) ?: 'EUR',
|
||||
'report_currency' => strtoupper(trim((string) ($payload['report_currency'] ?? ($existing['report_currency'] ?? 'EUR')))) ?: 'EUR',
|
||||
'crypto_currency' => strtoupper(trim((string) ($payload['crypto_currency'] ?? ($existing['crypto_currency'] ?? 'DOGE')))) ?: 'DOGE',
|
||||
'display_timezone' => $displayTimezone,
|
||||
'fx_max_age_hours' => isset($existing['fx_max_age_hours']) && is_numeric((string) $existing['fx_max_age_hours'])
|
||||
? (int) $existing['fx_max_age_hours']
|
||||
: 3,
|
||||
'module_theme_mode' => in_array((string) ($existing['module_theme_mode'] ?? 'inherit'), ['inherit', 'custom'], true)
|
||||
? (string) $existing['module_theme_mode']
|
||||
: 'inherit',
|
||||
'module_theme_accent' => in_array((string) ($existing['module_theme_accent'] ?? 'teal'), ['teal', 'logo', 'pink', 'cyan', 'orange', 'green'], true)
|
||||
? (string) $existing['module_theme_accent']
|
||||
: 'teal',
|
||||
'preferred_currencies' => $preferredCurrencies,
|
||||
];
|
||||
|
||||
$repository->saveSettings($projectKey, $settings);
|
||||
|
||||
if (modules()->isEnabled('fx-rates') && modules()->hasFunction('fx-rates', 'save_runtime_settings')) {
|
||||
module_fn('fx-rates', 'save_runtime_settings', [
|
||||
'preferred_currencies' => $preferredCurrencies,
|
||||
]);
|
||||
}
|
||||
|
||||
return module_fn($moduleName, 'runtime_settings');
|
||||
});
|
||||
|
||||
$mm->registerFunction('mining-checker', 'sql_import_target', static function (): array {
|
||||
$moduleBasePath = __DIR__;
|
||||
$config = ModuleConfig::load($moduleBasePath);
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
{
|
||||
"name": "Mining-Checker",
|
||||
"title": "Mining-Checker",
|
||||
"version": "0.3.0",
|
||||
"description": "Erfassung, OCR-Auswertung und Analyse von DOGE-Mining-Messwerten als eingebettetes Modul.",
|
||||
"enabled_by_default": true,
|
||||
"setup": {
|
||||
"fields": [
|
||||
{ "name": "baseline_measured_at", "label": "Baseline Zeitpunkt", "type": "datetime-local", "required": false, "help": "Referenzzeitpunkt fuer die erste Baseline-Messung." },
|
||||
{ "name": "baseline_coins_total", "label": "Baseline Coins", "type": "number", "required": false, "help": "Coin-Bestand zum Baseline-Zeitpunkt." },
|
||||
{ "name": "daily_cost_amount", "label": "Taegliche Kosten", "type": "number", "required": false, "help": "Taegliche Kosten fuer die Break-even-Berechnung." },
|
||||
{ "name": "daily_cost_currency", "label": "Kostenwaehrung", "type": "text", "required": false, "help": "FIAT-Waehrung der taeglichen Kosten, z.B. EUR." },
|
||||
{ "name": "report_currency", "label": "Standard-Berichtswaehrung", "type": "text", "required": false, "help": "Zielwaehrung fuer Kennzahlen und Berichte, z.B. EUR." },
|
||||
{ "name": "crypto_currency", "label": "Standard-Krypto-Waehrung", "type": "text", "required": false, "help": "Aktuell geminte Hauptwaehrung, z.B. DOGE oder BTC." },
|
||||
{ "name": "display_timezone", "label": "Anzeige-Zeitzone", "type": "text", "required": false, "help": "Zeitzone fuer lokale Datums- und Uhrzeitanzeige, z.B. Europe/Berlin." },
|
||||
{ "name": "preferred_currencies", "label": "Bevorzugte Waehrungen", "type": "textarea", "required": false, "help": "Mehrere Codes mit Komma oder Zeilenumbruch trennen, z.B. DOGE, USD, EUR." }
|
||||
]
|
||||
},
|
||||
"auth": {
|
||||
"required": true,
|
||||
"users": [],
|
||||
|
||||
@@ -48,6 +48,17 @@ foreach ($fields as $field) {
|
||||
}
|
||||
$isFxRatesSetup = $moduleName === 'fx-rates';
|
||||
$current = modules()->settings($moduleName);
|
||||
$runtimeSettingsEnabled = modules()->hasFunction($moduleName, 'runtime_settings');
|
||||
if ($runtimeSettingsEnabled) {
|
||||
try {
|
||||
$runtimeSettings = module_fn($moduleName, 'runtime_settings');
|
||||
if (is_array($runtimeSettings)) {
|
||||
$current = array_replace_recursive($current, $runtimeSettings);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
$intervalTaskStatuses = [];
|
||||
$cronTaskDefinitions = modules()->cronTasks($moduleName);
|
||||
$cronTaskStatuses = [];
|
||||
@@ -405,15 +416,23 @@ $renderField = function (array $field) use (&$current, $getNested, $driverOption
|
||||
$type = (string)($field['type'] ?? 'text');
|
||||
$required = !empty($field['required']);
|
||||
$help = (string)($field['help'] ?? $field['description'] ?? '');
|
||||
$options = is_array($field['options'] ?? null) ? $field['options'] : [];
|
||||
$postKey = str_replace('.', '_', $name);
|
||||
|
||||
$value = '';
|
||||
if ($name === 'kea_auto_init') {
|
||||
$value = !empty($current[$name]) ? '1' : '0';
|
||||
} elseif (str_contains($name, '.')) {
|
||||
$value = (string)($getNested($current, $name) ?? '');
|
||||
$value = $getNested($current, $name);
|
||||
} else {
|
||||
$value = (string)($current[$name] ?? '');
|
||||
$value = $current[$name] ?? '';
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = $type === 'multiselect'
|
||||
? array_values(array_map('strval', $value))
|
||||
: implode(', ', array_values(array_map('strval', $value)));
|
||||
} else {
|
||||
$value = (string) $value;
|
||||
}
|
||||
?>
|
||||
<label class="setup-field muted">
|
||||
@@ -425,6 +444,38 @@ $renderField = function (array $field) use (&$current, $getNested, $driverOption
|
||||
<option value="<?= e($driver) ?>" <?= $value === $driver ? 'selected' : '' ?>><?= e($driverLabel) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php elseif ($type === 'select' && $options !== []): ?>
|
||||
<select name="<?= e($postKey) ?>" <?= $required ? 'required' : '' ?>>
|
||||
<option value="">Bitte waehlen</option>
|
||||
<?php foreach ($options as $optionValue => $optionLabel): ?>
|
||||
<?php
|
||||
if (is_int($optionValue) && is_array($optionLabel)) {
|
||||
$optionValue = (string) ($optionLabel['value'] ?? '');
|
||||
$optionLabel = (string) ($optionLabel['label'] ?? $optionValue);
|
||||
} else {
|
||||
$optionValue = (string) $optionValue;
|
||||
$optionLabel = (string) $optionLabel;
|
||||
}
|
||||
?>
|
||||
<option value="<?= e($optionValue) ?>" <?= $value === $optionValue ? 'selected' : '' ?>><?= e($optionLabel) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php elseif ($type === 'multiselect' && $options !== []): ?>
|
||||
<?php $selectedValues = is_array($value) ? $value : []; ?>
|
||||
<select name="<?= e($postKey) ?>[]" multiple size="<?= e((string) min(8, max(4, count($options)))) ?>" <?= $required ? 'required' : '' ?>>
|
||||
<?php foreach ($options as $optionValue => $optionLabel): ?>
|
||||
<?php
|
||||
if (is_int($optionValue) && is_array($optionLabel)) {
|
||||
$optionValue = (string) ($optionLabel['value'] ?? '');
|
||||
$optionLabel = (string) ($optionLabel['label'] ?? $optionValue);
|
||||
} else {
|
||||
$optionValue = (string) $optionValue;
|
||||
$optionLabel = (string) $optionLabel;
|
||||
}
|
||||
?>
|
||||
<option value="<?= e($optionValue) ?>" <?= in_array($optionValue, $selectedValues, true) ? 'selected' : '' ?>><?= e($optionLabel) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php elseif ($type === 'textarea'): ?>
|
||||
<textarea name="<?= e($postKey) ?>" rows="3" <?= $required ? 'required' : '' ?>><?= e($value) ?></textarea>
|
||||
<?php elseif ($type === 'checkbox'): ?>
|
||||
@@ -620,9 +671,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'groups' => array_merge($selectedGroups, $manualGroupValues),
|
||||
]);
|
||||
}
|
||||
if ($isFxRatesSetup && modules()->hasFunction($moduleName, 'save_runtime_settings')) {
|
||||
if (modules()->hasFunction($moduleName, 'save_runtime_settings')) {
|
||||
module_fn($moduleName, 'save_runtime_settings', $payload);
|
||||
$current = modules()->settings($moduleName);
|
||||
if ($runtimeSettingsEnabled) {
|
||||
$runtimeSettings = module_fn($moduleName, 'runtime_settings');
|
||||
if (is_array($runtimeSettings)) {
|
||||
$current = array_replace_recursive($current, $runtimeSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
$refreshSchedulerState();
|
||||
if ($submittedSetupSection === 'general' && array_key_exists('debug_enabled', $payload) && empty($payload['debug_enabled'])) {
|
||||
|
||||
Reference in New Issue
Block a user