diff --git a/application/RaspberryDisplay/RaspberryDisplayController.php b/application/RaspberryDisplay/RaspberryDisplayController.php index 15d0839ff..fbdfaff0d 100644 --- a/application/RaspberryDisplay/RaspberryDisplayController.php +++ b/application/RaspberryDisplay/RaspberryDisplayController.php @@ -70,6 +70,9 @@ class RaspberryDisplayController extends mfBaseController { } protected function createDisplayApi(): array|bool { + $monitorSize = $this->request->monitor_size ?? '27'; + if (!in_array($monitorSize, ['27', '42', '55', '65'])) $monitorSize = '27'; + $data = [ 'display_label' => $this->request->display_label ?? '', 'hostname' => $this->request->hostname ?? '', @@ -77,7 +80,7 @@ class RaspberryDisplayController extends mfBaseController { 'display_url' => $this->request->display_url ?? '', 'group_name' => $this->request->group_name ?? '', 'group_order' => (int)($this->request->group_order ?? 0), - 'monitor_size' => $this->request->monitor_size ?? '27', + 'monitor_size' => $monitorSize, 'hdmi_port' => (int)($this->request->hdmi_port ?? 0), 'agent_port' => (int)($this->request->agent_port ?? 5000), 'custom_style' => $this->request->custom_style ?? null, @@ -234,10 +237,10 @@ class RaspberryDisplayController extends mfBaseController { "BASE_URL" => self::getUrl("RaspberryDisplay"), "DASHBOARD_URL" => self::getUrl("Dashboard"), "MFAPPNAME" => MFAPPNAME_SLUG, - "PAGE_TITLE" => "NOC Display Manager", + "PAGE_TITLE" => "NOC Display Verwaltung", "PATH" => [ ["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")], - ["text" => "NOC Display Manager", "href" => self::getUrl("RaspberryDisplay")] + ["text" => "NOC Display Verwaltung", "href" => self::getUrl("RaspberryDisplay")] ] ]); $this->layout()->set("additionalCSS", ["css/views/RaspberryDisplay.css"]); diff --git a/application/RaspberryDisplay/RaspberryDisplayModel.php b/application/RaspberryDisplay/RaspberryDisplayModel.php index 72ca9b3ec..8b8aa0726 100644 --- a/application/RaspberryDisplay/RaspberryDisplayModel.php +++ b/application/RaspberryDisplay/RaspberryDisplayModel.php @@ -114,11 +114,10 @@ class RaspberryDisplayModel } } - // Set defaults if ($model->group_order === null) { $model->group_order = 0; } - if ($model->monitor_size === null) { + if (!in_array($model->monitor_size, ['27', '42', '55', '65'])) { $model->monitor_size = '27'; } if ($model->hdmi_port === null) { @@ -166,15 +165,30 @@ class RaspberryDisplayModel { $db = FronkDB::singleton(); - $data = $model->data; + $data = [ + 'display_label' => $model->display_label, + 'hostname' => $model->hostname, + 'ip_address' => $model->ip_address, + 'display_url' => $model->display_url, + 'group_name' => $model->group_name, + 'group_order' => (int)$model->group_order, + 'monitor_size' => in_array($model->monitor_size, ['27', '42', '55', '65']) ? $model->monitor_size : '27', + 'hdmi_port' => (int)$model->hdmi_port, + 'agent_port' => (int)$model->agent_port, + 'custom_style' => $model->custom_style, + 'create_by' => $model->create_by, + 'edit_by' => $model->edit_by, + ]; + + $forceStr = ['monitor_size']; if ($model->id) { $data['edit'] = time(); - $db->update("RaspberryDisplay", $data, "id=" . (int)$model->id); + $db->update("RaspberryDisplay", $data, "id=" . (int)$model->id, $forceStr); } else { $data['create'] = time(); $data['edit'] = time(); - $model->id = $db->insert("RaspberryDisplay", $data); + $model->id = $db->insert("RaspberryDisplay", $data, $forceStr); } return $model; diff --git a/public/css/views/RaspberryDisplay.css b/public/css/views/RaspberryDisplay.css index 168b686b7..9d1a3a5fa 100644 --- a/public/css/views/RaspberryDisplay.css +++ b/public/css/views/RaspberryDisplay.css @@ -11,13 +11,20 @@ --noc-drag-shadow: 0 12px 32px rgba(0, 83, 132, 0.2); } -/* Main Container */ .noc-display-manager { padding: 20px; max-width: 1600px; margin: 0 auto; } +.noc-main-card { + background: var(--tt-card, #fff); + border: 1px solid var(--tt-border, #e9ecef); + border-radius: 12px; + padding: 24px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); +} + /* Header */ .noc-header { display: flex; @@ -46,9 +53,8 @@ gap: 16px; margin-bottom: 24px; padding: 12px 16px; - background: var(--tt-card, #fff); - border-radius: 12px; - border: 1px solid var(--tt-border, #e9ecef); + background: var(--tt-card-2, #f8f9fa); + border-radius: 8px; flex-wrap: wrap; } @@ -628,6 +634,11 @@ transition: background 0.15s, border-color 0.15s, color 0.15s; } +.tt-scope .ghost-btn i, +.tt-scope .primary-btn i { + font-size: 13px; +} + .tt-scope .ghost-btn:hover { background: var(--tt-card-2, #f8f9fa); border-color: var(--tt-accent, #005384); diff --git a/public/js/pages/RaspberryDisplay/RaspberryDisplayApp.js b/public/js/pages/RaspberryDisplay/RaspberryDisplayApp.js index d05bf3241..8759e426c 100644 --- a/public/js/pages/RaspberryDisplay/RaspberryDisplayApp.js +++ b/public/js/pages/RaspberryDisplay/RaspberryDisplayApp.js @@ -1,205 +1,190 @@ -// NOC Display Manager - Vue 3 Component const RaspberryDisplay = { name: 'RaspberryDisplay', template: `
- -
-

NOC Display Manager

-
- -
-
- - -
- -
- - - - -
-
- - -
- -
- - -
- -

No Displays Configured

-

Add your first display to get started

- -
- - -
-
- -
-
- - {{ groupName }} - {{ displays.length }} -
-
- -
-
- - -
-
- -
- -
- - -
-
- {{ display.monitor_size }}" -
- - -
-
{{ display.display_label }}
-
- HDMI:{{ display.hdmi_port }} - {{ display.ip_address }} -
-
- - -
-
- - -
- - - {{ displayStatuses[display.ip_address]?.temperature_c?.toFixed(1) || '--' }}°C - - - - {{ displayStatuses[display.ip_address]?.cpu_percent?.toFixed(0) || '--' }}% - -
- - -
- - - - -
-
- - -
- Drop displays here -
+
+
+

NOC Display Verwaltung

+
+
- -
- - Add Group +
+ +
+ + + + +
+
+ +
+ +
+ +
+ +

Keine Displays konfiguriert

+

Fügen Sie Ihr erstes Display hinzu, um zu beginnen

+ +
+ +
+
+
+
+ + {{ groupName }} + {{ displays.length }} +
+
+ +
+
+ +
+
+
+ +
+ +
+
+ {{ display.monitor_size }}" +
+ +
+
{{ display.display_label }}
+
+ HDMI:{{ display.hdmi_port }} + {{ display.ip_address }} +
+
+ + +
+
+ +
+ + + {{ displayStatuses[display.ip_address]?.temperature_c?.toFixed(1) || '--' }}°C + + + + {{ displayStatuses[display.ip_address]?.cpu_percent?.toFixed(0) || '--' }}% + +
+ +
+ + + + +
+
+ +
+ Displays hierher ziehen +
+
+
+ +
+ + Gruppe hinzufügen +
- - +
-
-

Discover Raspberry Pi

+

Raspberry Pi suchen

- +
- {{ discoveredPi.hostname || 'Raspberry Pi' }} found + {{ discoveredPi.hostname || 'Raspberry Pi' }} gefunden
HDMI:{{ disp.hdmi_port }} - {{ truncateUrl(disp.current_url) || 'No URL' }} + {{ truncateUrl(disp.current_url) || 'Keine URL' }}
@@ -210,14 +195,13 @@ const RaspberryDisplay = {
-
- - + +
- +
@@ -231,12 +215,12 @@ const RaspberryDisplay = {
- + - +
@@ -247,7 +231,7 @@ const RaspberryDisplay = {
- + + +
- - -

Are you sure you want to delete {{ deleteTarget?.display_label }}?

-

This action cannot be undone.

+ +

Möchten Sie {{ deleteTarget?.display_label }} wirklich löschen?

+

Diese Aktion kann nicht rückgängig gemacht werden.

-
- Refreshing Displays + Displays werden aktualisiert
@@ -400,7 +381,7 @@ const RaspberryDisplay = { async loadDisplays() { try { - const response = await axios.get(window.TT_CONFIG.BASE_URL + 'api', { + const response = await axios.get(window.TT_CONFIG.BASE_URL + '/api', { params: { do: 'getDisplays' } }); if (response.data.status === 'OK') { @@ -416,7 +397,7 @@ const RaspberryDisplay = { async loadStatuses() { try { - const response = await axios.get(window.TT_CONFIG.BASE_URL + 'api', { + const response = await axios.get(window.TT_CONFIG.BASE_URL + '/api', { params: { do: 'getBatchStatus' } }); if (response.data.status === 'OK') { @@ -506,13 +487,13 @@ const RaspberryDisplay = { if (groupName === '__new__') { groupName = this.newGroupName.trim(); if (!groupName) { - alert('Please enter a group name'); + alert('Bitte geben Sie einen Gruppennamen ein'); return; } } if (!this.formData.display_label || !this.formData.ip_address || !groupName) { - alert('Please fill in all required fields'); + alert('Bitte füllen Sie alle Pflichtfelder aus'); return; } @@ -528,16 +509,16 @@ const RaspberryDisplay = { params.id = this.editingDisplay.id; } - const response = await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { params }); + const response = await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params }); if (response.data.status === 'OK' || response.data.status === 'success') { this.closeDisplayModal(); await this.loadDisplays(); } else { - alert('Failed to save display'); + alert('Speichern fehlgeschlagen'); } } catch (error) { console.error('Failed to save display:', error); - alert('Failed to save display'); + alert('Speichern fehlgeschlagen'); } finally { this.saving = false; } @@ -553,7 +534,7 @@ const RaspberryDisplay = { this.deleting = true; try { - const response = await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + const response = await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'deleteDisplay', id: this.deleteTarget.id } }); if (response.data.status === 'OK' || response.data.status === 'success') { @@ -577,7 +558,7 @@ const RaspberryDisplay = { this.discoverError = ''; try { - const response = await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + const response = await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'discoverPi', ip_address: this.discoverIp, @@ -590,10 +571,10 @@ const RaspberryDisplay = { this.formData.agent_port = this.discoverPort; this.formData.hostname = this.discoveredPi.hostname || ''; } else { - this.discoverError = response.data.result?.error || 'Could not connect to Raspberry Pi'; + this.discoverError = response.data.result?.error || 'Verbindung zum Raspberry Pi fehlgeschlagen'; } } catch (error) { - this.discoverError = 'Failed to discover Pi'; + this.discoverError = 'Suche fehlgeschlagen'; } finally { this.discovering = false; } @@ -633,7 +614,7 @@ const RaspberryDisplay = { } try { - const response = await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + const response = await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'setUrl', id: display.id, @@ -654,7 +635,7 @@ const RaspberryDisplay = { async refreshDisplay(display) { this.refreshingDisplays[display.id] = true; try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'refreshDisplay', id: display.id } }); } catch (error) { @@ -669,7 +650,7 @@ const RaspberryDisplay = { const state = status?.displays?.[display.hdmi_port]?.cec_state === 'on' ? 'off' : 'on'; try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'cecPower', id: display.id, state } }); await this.loadStatuses(); @@ -691,7 +672,7 @@ const RaspberryDisplay = { this.refreshingDisplays[display.id] = true; try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'refreshDisplay', id: display.id } }); await new Promise(resolve => setTimeout(resolve, 2000)); @@ -717,7 +698,7 @@ const RaspberryDisplay = { async powerAllDisplays(state) { this.poweringAll = true; try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'powerAll', state } }); await this.loadStatuses(); @@ -729,11 +710,11 @@ const RaspberryDisplay = { }, async rebootAllPis() { - if (!confirm('Are you sure you want to reboot all Raspberry Pis?')) return; + if (!confirm('Möchten Sie wirklich alle Raspberry Pis neustarten?')) return; this.rebootingAll = true; try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'rebootAll' } }); } catch (error) { @@ -788,7 +769,7 @@ const RaspberryDisplay = { // Persist to server try { - await axios.post(window.TT_CONFIG.BASE_URL + 'api', null, { + await axios.post(window.TT_CONFIG.BASE_URL + '/api', null, { params: { do: 'updateDisplay', id: display.id,