diff --git a/config/apps.php b/config/apps.php index 4d0f1be6..31f9e9d7 100644 --- a/config/apps.php +++ b/config/apps.php @@ -74,12 +74,24 @@ return [ 'icon' => 'MC', 'entry_route' => '/apps/mining-checker', 'window_mode' => 'window', - 'content_mode' => 'iframe', + 'content_mode' => 'native-module', 'default_width' => 1180, 'default_height' => 760, 'supports_widget' => false, 'supports_tray' => true, 'module_name' => 'finance', 'summary' => 'Vollstaendiger Rechner fuer Mining-Profitabilitaet, Stromkosten, ROI und Snapshot-Historie.', + 'module_options' => [ + 'default_project_key' => 'doge-main', + 'active_view' => 'overview', + 'sections' => [ + ['key' => 'overview', 'label' => 'Übersicht'], + ['key' => 'upload', 'label' => 'Upload'], + ['key' => 'measurements', 'label' => 'Mining-History'], + ['key' => 'wallet', 'label' => 'Wallet'], + ['key' => 'mining', 'label' => 'Miner-Daten'], + ['key' => 'dashboards', 'label' => 'Dashboards'], + ], + ], ], ]; diff --git a/partials/desktop/shell.php b/partials/desktop/shell.php index 1fb1422e..55a7f53c 100644 --- a/partials/desktop/shell.php +++ b/partials/desktop/shell.php @@ -20,6 +20,14 @@ $assetVersion = static function (string $publicPath): string { return $mtime === false ? $publicPath : $publicPath . '?v=' . $mtime; }; +$hasMiningChecker = false; +foreach ($desktopPayload['apps'] as $desktopApp) { + if (($desktopApp['app_id'] ?? null) === 'mining-checker') { + $hasMiningChecker = true; + break; + } +} + $renderStartIcon = static function (array $profile): string { $style = (string) ($profile['taskbar_icon_style'] ?? 'label'); @@ -142,5 +150,11 @@ $renderStartIcon = static function (array $profile): string { + + + + + + diff --git a/public/assets/apps/mining-checker/app.js b/public/assets/apps/mining-checker/app.js index 14782404..b739b510 100644 --- a/public/assets/apps/mining-checker/app.js +++ b/public/assets/apps/mining-checker/app.js @@ -1,32 +1,33 @@ (function () { - const root = document.getElementById('mining-checker-app'); - if (!root || !window.React || !window.ReactDOM) { - return; - } + function boot(root) { + if (!root || !window.React || !window.ReactDOM) { + return false; + } - const h = React.createElement; - const { useEffect, useMemo, useRef, useState } = React; - const apiBase = root.dataset.apiBase || '/api/mining-checker/v1'; - const initialProjectKey = root.dataset.defaultProjectKey || 'doge-main'; - const initialActiveTab = String(root.dataset.activeView || 'overview').trim() || 'overview'; - const configuredSections = (() => { - try { - const parsed = JSON.parse(root.dataset.sectionsJson || '[]'); - if (!Array.isArray(parsed)) { + const h = React.createElement; + const { useEffect, useMemo, useRef, useState } = React; + const apiBase = root.dataset.apiBase || '/api/mining-checker/v1'; + const initialProjectKey = root.dataset.defaultProjectKey || 'doge-main'; + const initialActiveTab = String(root.dataset.activeView || 'overview').trim() || 'overview'; + const configuredSections = (() => { + try { + const parsed = JSON.parse(root.dataset.sectionsJson || '[]'); + if (!Array.isArray(parsed)) { + return []; + } + return parsed + .map((section) => { + const key = section && typeof section.key === 'string' ? section.key.trim() : ''; + const label = section && typeof section.label === 'string' ? section.label.trim() : ''; + return key && label ? [key, label] : null; + }) + .filter(Boolean); + } catch (error) { return []; } - return parsed - .map((section) => { - const key = section && typeof section.key === 'string' ? section.key.trim() : ''; - const label = section && typeof section.label === 'string' ? section.label.trim() : ''; - return key && label ? [key, label] : null; - }) - .filter(Boolean); - } catch (error) { - return []; - } - })(); - const initialDebugMode = document.body && document.body.dataset.moduleDebugEnabled === '1'; + })(); + const initialDebugMode = root.dataset.moduleDebugEnabled === '1' + || (document.body && document.body.dataset.moduleDebugEnabled === '1'); function getCookie(name) { const pattern = `; ${document.cookie}`; const parts = pattern.split(`; ${name}=`); @@ -3184,5 +3185,47 @@ } } - ReactDOM.createRoot(root).render(h(App)); + ReactDOM.createRoot(root).render(h(App)); + return true; + } + + window.initMiningCheckerApp = function initMiningCheckerApp(root, options) { + if (!(root instanceof HTMLElement)) { + return false; + } + + const config = options && typeof options === 'object' ? options : {}; + + if (config.apiBase) { + root.dataset.apiBase = String(config.apiBase); + } + if (config.defaultProjectKey) { + root.dataset.defaultProjectKey = String(config.defaultProjectKey); + } + if (config.activeView) { + root.dataset.activeView = String(config.activeView); + } + if (config.sections) { + root.dataset.sectionsJson = JSON.stringify(config.sections); + } + if (config.moduleDebugEnabled !== undefined) { + root.dataset.moduleDebugEnabled = config.moduleDebugEnabled ? '1' : '0'; + } + + if (root.dataset.miningCheckerMounted === '1') { + return true; + } + + const mounted = boot(root); + if (mounted) { + root.dataset.miningCheckerMounted = '1'; + } + + return mounted; + }; + + const root = document.getElementById('mining-checker-app'); + if (root) { + window.initMiningCheckerApp(root); + } })(); diff --git a/public/assets/desktop/desktop.css b/public/assets/desktop/desktop.css index baf40efd..6e69a957 100644 --- a/public/assets/desktop/desktop.css +++ b/public/assets/desktop/desktop.css @@ -410,6 +410,22 @@ h1 { background: #ffffff; } +.window-content--module { + position: relative; + background: #f8fafc; +} + +.window-module-host { + position: absolute; + inset: 0; + overflow: auto; +} + +.window-module-error { + padding: 18px; + color: #991b1b; +} + .window-embed-fallback { position: absolute; inset: auto 16px 16px 16px; diff --git a/public/assets/desktop/desktop.js b/public/assets/desktop/desktop.js index 78b95357..9d69eadd 100644 --- a/public/assets/desktop/desktop.js +++ b/public/assets/desktop/desktop.js @@ -108,6 +108,14 @@ if (payloadNode) { }; const buildWindowBody = (definition) => { + if (definition.content_mode === 'native-module' && definition.app_id === 'mining-checker') { + return ` +
+
+
+ `; + } + if (definition.content_mode === 'iframe' && typeof definition.entry_route === 'string' && definition.entry_route !== '') { const title = escapeHtml(definition.title); const src = escapeHtml(definition.entry_route); @@ -544,6 +552,22 @@ if (payloadNode) { }, { once: true }); } + const moduleHost = node.querySelector('.window-module-host'); + if (moduleHost && definition.content_mode === 'native-module' && definition.app_id === 'mining-checker') { + const moduleOptions = definition.module_options || {}; + if (typeof window.initMiningCheckerApp === 'function') { + window.initMiningCheckerApp(moduleHost, { + apiBase: '/api/mining-checker/index.php/v1', + defaultProjectKey: moduleOptions.default_project_key || 'doge-main', + activeView: moduleOptions.active_view || 'overview', + sections: Array.isArray(moduleOptions.sections) ? moduleOptions.sections : [], + moduleDebugEnabled: false, + }); + } else { + moduleHost.innerHTML = '
Mining-Checker Assets konnten nicht initialisiert werden.
'; + } + } + node.querySelectorAll('.window-action').forEach((action) => { action.addEventListener('click', (event) => { event.stopPropagation(); @@ -595,6 +619,7 @@ if (payloadNode) { content: app.summary || '', entry_route: app.entry_route || '', content_mode: app.content_mode || 'summary', + module_options: app.module_options || null, }); };