diff --git a/application/controllers/Test.php b/application/controllers/Test.php index 497f12f85..bbb759606 100644 --- a/application/controllers/Test.php +++ b/application/controllers/Test.php @@ -59,5 +59,4 @@ class Test extends Auth_Controller if (!$this->_uid) show_error('User authentification failed'); } - } diff --git a/application/controllers/dashboard/Api.php b/application/controllers/dashboard/Api.php index c66f181dc..422bf0675 100644 --- a/application/controllers/dashboard/Api.php +++ b/application/controllers/dashboard/Api.php @@ -9,7 +9,7 @@ class Api extends Auth_Controller array( 'index' => 'dashboard/admin:rw', 'getNews' => 'dashboard/benutzer:r', - 'getAmpeln' => 'dashboard/benutzer:r', + 'getAmpeln' => 'dashboard/benutzer:r', ) ); @@ -34,9 +34,8 @@ class Api extends Auth_Controller $result = $this->NewsModel->getAll($limit); - if(hasData($result)) + if (hasData($result)) { - $this->outputJson(getData($result), REST_Controller::HTTP_OK); } else @@ -49,21 +48,20 @@ class Api extends Auth_Controller /** * Get Ampeln. */ - public function getAmpeln(){ + public function getAmpeln() + { $this->load->model('content/Ampel_model', 'AmpelModel'); $result = $this->AmpelModel->getByUser($this->_uid); - if(hasData($result)) + if (hasData($result)) { - $this->outputJson(getData($result), REST_Controller::HTTP_OK); } else { $this->terminateWithJsonError('fehler entdeckt'); } - } /** diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index b9c4c6c31..2c0cf5fca 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -18,9 +18,9 @@ class Config extends Auth_Controller 'removeWidgetFromPreset' => 'dashboard/admin:rw', 'addWidgetsToUserOverride' => 'dashboard/benutzer:rw', 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw', - 'Funktionen' => 'dashboard/admin:r', - 'Preset' => 'dashboard/admin:r', - 'PresetBatch' => 'dashboard/admin:r' + 'funktionen' => 'dashboard/admin:r', + 'preset' => 'dashboard/admin:r', + 'presetBatch' => 'dashboard/admin:r' ) ); @@ -29,7 +29,7 @@ class Config extends Auth_Controller $this->load->model('ressource/Funktion_model', 'FunktionModel'); } - public function index() + public function index() { $dashboard_kurzbz = $this->input->get('db'); $uid = $this->AuthLib->getAuthObj()->username; @@ -46,7 +46,7 @@ class Config extends Auth_Controller $this->outputJsonSuccess($mergedconfig); } - public function genWidgetId() + public function genWidgetId() { $dashboard_kurzbz = $this->input->get('db'); $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); @@ -55,23 +55,22 @@ class Config extends Auth_Controller )); } - public function addWidgetsToPreset() + public function addWidgetsToPreset() { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; $funktion_kurzbz = $input->funktion_kurzbz; - $preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz); + $preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz); $preset_decoded = json_decode($preset->preset, true); - $this->DashboardLib->addWidgetsToWidgets($preset_decoded['widgets'], - $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); + $this->DashboardLib->addWidgetsToWidgets($preset_decoded['widgets'], $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); $preset->preset = json_encode($preset_decoded); $result = $this->DashboardLib->insertOrUpdatePreset($preset); - if( isError($result) ) { + if (isError($result)) { http_response_code(500); $this->terminateWithJsonError('preset could not be saved'); } @@ -87,16 +86,13 @@ class Config extends Auth_Controller $widgetid = $input->widgetid; $preset = $this->DashboardLib->getPreset($dashboard_kurzbz, $funktion_kurzbz); - if( $preset === null ) { + if ($preset === null) { http_response_code(404); - $this->terminateWithJsonError('preset for dashboard ' - . $dashboard_kurzbz . ' and funktion ' . $funktion_kurzbz - . ' not found.'); + $this->terminateWithJsonError('preset for dashboard ' . $dashboard_kurzbz . ' and funktion ' . $funktion_kurzbz . ' not found.'); } $preset_decoded = json_decode($preset->preset, true); - if (!$this->DashboardLib->removeWidgetFromWidgets($preset_decoded['widgets'], - $funktion_kurzbz, $widgetid)) + if (!$this->DashboardLib->removeWidgetFromWidgets($preset_decoded['widgets'], $funktion_kurzbz, $widgetid)) { http_response_code(404); $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); @@ -104,32 +100,31 @@ class Config extends Auth_Controller $preset->preset = json_encode($preset_decoded); $result = $this->DashboardLib->insertOrUpdatePreset($preset); - if( isError($result) ) + if (isError($result)) { http_response_code(500); - $this->terminateWithJsonError('failed to remove widget'); + $this->terminateWithJsonError('failed to remove widget'); } $this->outputJsonSuccess(array('msg' => 'preset successfully updated.')); } - public function addWidgetsToUserOverride() + public function addWidgetsToUserOverride() { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; $funktion_kurzbz = $input->funktion_kurzbz; $uid = $this->AuthLib->getAuthObj()->username; - $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); + $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); $override_decoded = json_decode($override->override, true); - $this->DashboardLib->addWidgetsToWidgets($override_decoded['widgets'], - $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); + $this->DashboardLib->addWidgetsToWidgets($override_decoded['widgets'], $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); $override->override = json_encode($override_decoded); $result = $this->DashboardLib->insertOrUpdateOverride($override); - if( isError($result) ) { + if (isError($result)) { http_response_code(500); $this->terminateWithJsonError('override could not be saved'); } @@ -146,16 +141,14 @@ class Config extends Auth_Controller $widgetid = $input->widgetid; $override = $this->DashboardLib->getOverride($dashboard_kurzbz, $uid); - if( empty($override) ) { + if (empty($override)) { http_response_code(404); - $this->terminateWithJsonError('userconfig for dashboard ' - . $dashboard_kurzbz . ' not found.'); + $this->terminateWithJsonError('userconfig for dashboard ' . $dashboard_kurzbz . ' not found.'); } $override_decoded = json_decode($override->override, true); - if( !$this->DashboardLib->removeWidgetFromWidgets($override_decoded['widgets'], - $funktion_kurzbz, $widgetid) ) + if (!$this->DashboardLib->removeWidgetFromWidgets($override_decoded['widgets'], $funktion_kurzbz, $widgetid)) { http_response_code(404); $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); @@ -163,15 +156,15 @@ class Config extends Auth_Controller $override->override = json_encode($override_decoded); $result = $this->DashboardLib->insertOrUpdateOverride($override, $uid); - if( isError($result) ) + if (isError($result)) { http_response_code(500); - $this->terminateWithJsonError('failed to remove widget'); + $this->terminateWithJsonError('failed to remove widget'); } $this->outputJsonSuccess(array('msg' => 'override successfully updated.')); } - public function Funktionen() + public function funktionen() { $funktionen = $this->FunktionModel->load(); @@ -185,7 +178,7 @@ class Config extends Auth_Controller return $this->outputJsonSuccess(getData($funktionen) ?: []); } - public function Preset() + public function preset() { $db = $this->input->get('db'); $funktion = $this->input->get('funktion'); @@ -198,7 +191,7 @@ class Config extends Auth_Controller return $this->outputJsonSuccess(json_decode($conf->preset, true)); } - public function PresetBatch() + public function presetBatch() { $db = $this->input->get('db'); $funktionen = $this->input->get('funktionen'); @@ -220,5 +213,4 @@ class Config extends Auth_Controller return $this->outputJsonSuccess($result); } - } diff --git a/application/controllers/dashboard/Dashboard.php b/application/controllers/dashboard/Dashboard.php index 8cf37d579..3773a6d73 100644 --- a/application/controllers/dashboard/Dashboard.php +++ b/application/controllers/dashboard/Dashboard.php @@ -12,9 +12,9 @@ class Dashboard extends Auth_Controller parent::__construct( array( 'index' => 'dashboard/admin:r', - 'Create' => 'dashboard/admin:rw', - 'Update' => 'dashboard/admin:rw', - 'Delete' => 'dashboard/admin:rw' + 'create' => 'dashboard/admin:rw', + 'update' => 'dashboard/admin:rw', + 'delete' => 'dashboard/admin:rw' ) ); @@ -22,7 +22,7 @@ class Dashboard extends Auth_Controller $this->load->model('dashboard/Dashboard_model', 'DashboardModel'); } - public function index() + public function index() { $result = $this->DashboardModel->load(); @@ -36,7 +36,7 @@ class Dashboard extends Auth_Controller return $this->outputJsonSuccess(getData($result) ?: []); } - public function Create() + public function create() { $input = $this->getPostJSON(); @@ -52,7 +52,7 @@ class Dashboard extends Auth_Controller return $this->outputJsonSuccess(getData($result) ?: []); } - public function Update() + public function update() { $input = $this->getPostJSON(); @@ -68,7 +68,7 @@ class Dashboard extends Auth_Controller return $this->outputJsonSuccess(getData($result) ?: []); } - public function Delete() + public function delete() { $input = $this->getPostJSON(); @@ -83,5 +83,4 @@ class Dashboard extends Auth_Controller return $this->outputJsonSuccess(getData($result) ?: []); } - } diff --git a/application/controllers/dashboard/DashboardDemo.php b/application/controllers/dashboard/DashboardDemo.php index 7088c44b9..90eb017b8 100644 --- a/application/controllers/dashboard/DashboardDemo.php +++ b/application/controllers/dashboard/DashboardDemo.php @@ -17,6 +17,7 @@ class DashboardDemo extends Auth_Controller parent::__construct( array( 'index' => 'user:r', + 'admin' => 'dashboard/admin:rw' ) ); @@ -35,6 +36,13 @@ class DashboardDemo extends Auth_Controller $this->load->view('dashboard/dashboard_demo.php', []); } + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + public function admin() + { + $this->load->view('dashboard/dashboard_demo_admin.php', []); + } + // ----------------------------------------------------------------------------------------------------------------- // Private methods @@ -47,5 +55,4 @@ class DashboardDemo extends Auth_Controller if (!$this->_uid) show_error('User authentification failed'); } - } diff --git a/application/controllers/dashboard/Widget.php b/application/controllers/dashboard/Widget.php index 07c57ea73..0da6fe8da 100644 --- a/application/controllers/dashboard/Widget.php +++ b/application/controllers/dashboard/Widget.php @@ -23,7 +23,7 @@ class Widget extends Auth_Controller $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); } - public function index() + public function index() { $widget_id = $this->input->get('id'); @@ -48,7 +48,7 @@ class Widget extends Auth_Controller return $this->outputJsonSuccess(current(getData($widget))); } - public function getAll() + public function getAll() { $dashboard_id = $this->input->get('dashboard_id'); $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id); @@ -59,7 +59,7 @@ class Widget extends Auth_Controller $this->outputJsonSuccess(getData($result) ?: []); } - public function getWidgetsForDashboard() + public function getWidgetsForDashboard() { $db = $this->input->get('db'); $result = $this->WidgetModel->getForDashboard($db); @@ -74,7 +74,8 @@ class Widget extends Auth_Controller $this->outputJsonSuccess(getData($result) ?: []); } - public function setAllowed() { + public function setAllowed() + { $input = $this->getPostJSON(); $dashboard_id = $input->dashboard_id; @@ -83,12 +84,12 @@ class Widget extends Auth_Controller if ($action == 'add') { $result = $this->DashboardWidgetModel->insert([ - 'dashboard_id' => $dashboard_id, + 'dashboard_id' => $dashboard_id, 'widget_id' => $widget_id ]); } elseif ($action == 'delete') { $result = $this->DashboardWidgetModel->delete([ - 'dashboard_id' => $dashboard_id, + 'dashboard_id' => $dashboard_id, 'widget_id' => $widget_id ]); } else { diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index 3f189fc03..edea7c310 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -1,5 +1,8 @@ _ci =& get_instance(); @@ -24,28 +27,27 @@ class DashboardLib $this->_ci->load->model('dashboard/Dashboard_Override_model', 'DashboardOverrideModel'); } - public function generateWidgetId($dashboard_kurzbz='') + public function generateWidgetId($dashboard_kurzbz = '') { - $dashboard_kurzbz = (!empty($dashboard_kurzbz)) ? $dashboard_kurzbz - : self::DEFAULT_DASHBOARD_KURZBZ; - $widgetid_input = time() . '_' . $dashboard_kurzbz . '_' - . bin2hex(random_bytes(self::WIDGET_ID_RANDOM_BYTES)); + $dashboard_kurzbz = (!empty($dashboard_kurzbz)) ? $dashboard_kurzbz : self::DEFAULT_DASHBOARD_KURZBZ; + $widgetid_input = time() . '_' . $dashboard_kurzbz . '_' . bin2hex(random_bytes(self::WIDGET_ID_RANDOM_BYTES)); $widgetid = md5($widgetid_input); return $widgetid; } - public function getDashboardByKurzbz($dashboard_kurzbz) + public function getDashboardByKurzbz($dashboard_kurzbz) { - $dashboard = null; $result = $this->_ci->DashboardModel->getDashboardByKurzbz($dashboard_kurzbz); - if( isSuccess($result) && ($dashboards = getData($result)) ) + + if (hasData($result)) { - $dashboard = $dashboards[0]; + return current(getData($result)); } - return $dashboard; + + return null; } - public function getMergedConfig($dashboard_id, $uid) + public function getMergedConfig($dashboard_id, $uid) { $defaultconfig = $this->getDefaultConfig($dashboard_id, $uid); $userconfig = $this->getUserConfig($dashboard_id, $uid); @@ -60,15 +62,15 @@ class DashboardLib $res_presets = $this->_ci->DashboardPresetModel->getPresets($dashboard_id, $uid); $defaultconfig = array(); - if( isSuccess($res_presets) && hasData($res_presets) ) + if (hasData($res_presets)) { $presets = getData($res_presets); foreach ($presets as $presetobj) { - if( null !== ($preset = json_decode($presetobj->preset, true)) ) + $preset = json_decode($presetobj->preset, true); + if (null !== $preset) { - $defaultconfig = array_replace_recursive($defaultconfig, - $preset); + $defaultconfig = array_replace_recursive($defaultconfig, $preset); } } } @@ -79,25 +81,25 @@ class DashboardLib public function getUserConfig($dashboard_id, $uid) { $res_userconfig = $this->_ci->DashboardOverrideModel->getOverride($dashboard_id, $uid); - $userconfig = array(); - if( isSuccess($res_userconfig) && hasData($res_userconfig) ) + if (hasData($res_userconfig)) { $data = getData($res_userconfig); - if( null !== ($decodedconfig = json_decode($data[0]->override, true)) ) + $decodedconfig = json_decode(current($data)->override, true); + if (null !== $decodedconfig) { - $userconfig = $decodedconfig; + return $decodedconfig; } } - return $userconfig; + return []; } - public function getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid) + public function getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid) { $override = $this->getOverride($dashboard_kurzbz, $uid); - if( null !== $override ) { - return $override; + if (null !== $override) { + return $override; } $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); @@ -110,11 +112,13 @@ class DashboardLib return $emptyoverride; } - public function getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz) + public function getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz) { + if ($funktion_kurzbz === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) + $funktion_kurzbz = null; $preset = $this->getPreset($dashboard_kurzbz, $funktion_kurzbz); - if( null !== $preset ) { - return $preset; + if (null !== $preset) { + return $preset; } $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); @@ -123,50 +127,49 @@ class DashboardLib $emptypreset->dashboard_id = $dashboard->dashboard_id; $emptypreset->funktion_kurzbz = $funktion_kurzbz; $section = ($funktion_kurzbz !== null) ? $funktion_kurzbz : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - $emptypreset->preset = '{"widgets": {"' . $funktion_kurzbz . '": {}}}'; + $emptypreset->preset = '{"widgets": {"' . $section . '": {}}}'; return $emptypreset; } - public function getPreset($dashboard_kurzbz, $section) + public function getPreset($dashboard_kurzbz, $section) { $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); - $preset = null; $funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section; $result = $this->_ci->DashboardPresetModel ->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz); - if( isSuccess($result) && hasData($result) && ($presets = getData($result)) ) + if (hasData($result)) { - $preset = $presets[0]; + return current(getData($result)); } - return $preset; + return null; } - public function getOverride($dashboard_kurzbz, $uid) + public function getOverride($dashboard_kurzbz, $uid) { $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); - $override = null; $result = $this->_ci->DashboardOverrideModel ->getOverride($dashboard->dashboard_id, $uid); - if( isSuccess($result) && hasData($result) && ($overrides = getData($result)) ) + if (hasData($result)) { - $override = $overrides[0]; + return current(getData($result)); } - return $override; + return null; } - public function insertOrUpdatePreset($preset) + public function insertOrUpdatePreset($preset) { - if( isset($preset->preset_id) && $preset->preset_id > 0 ) + if (isset($preset->preset_id) && $preset->preset_id > 0) { $result = $this->_ci->DashboardPresetModel->update($preset->preset_id, $preset); - } else + } + else { $result = $this->_ci->DashboardPresetModel->insert($preset); } @@ -174,12 +177,13 @@ class DashboardLib return $result; } - public function insertOrUpdateOverride($override) + public function insertOrUpdateOverride($override) { - if( isset($override->override_id) && $override->override_id > 0 ) + if (isset($override->override_id) && $override->override_id > 0) { $result = $this->_ci->DashboardOverrideModel->update($override->override_id, $override); - } else + } + else { $result = $this->_ci->DashboardOverrideModel->insert($override); } @@ -199,10 +203,10 @@ class DashboardLib } } - public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid) + public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid) { $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - if( !isset($widgets[$section]) || !is_array($widgets[$section]) ) + if (!isset($widgets[$section]) || !is_array($widgets[$section])) { $widgets[$section] = array(); } @@ -210,10 +214,10 @@ class DashboardLib $widgets[$section][$widgetid] = $widget; } - public function removeWidgetFromWidgets(&$widgets, $section, $widgetid) + public function removeWidgetFromWidgets(&$widgets, $section, $widgetid) { - $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - if(isset($widgets[$section]) && isset($widgets[$section][$widgetid]) ) + $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; + if (isset($widgets[$section]) && isset($widgets[$section][$widgetid])) { unset($widgets[$section][$widgetid]); if(empty($widgets[$section]) && $section !== self::USEROVERRIDE_SECTION) { diff --git a/application/models/dashboard/Dashboard_Preset_model.php b/application/models/dashboard/Dashboard_Preset_model.php index c899195d4..ca10ce98a 100644 --- a/application/models/dashboard/Dashboard_Preset_model.php +++ b/application/models/dashboard/Dashboard_Preset_model.php @@ -60,7 +60,7 @@ EOSQL; * @param string funktion_kurzbz * @return array */ - public function getPresetByDashboardAndFunktion($dashboard_id, $funktion_kurzbz) + public function getPresetByDashboardAndFunktion($dashboard_id, $funktion_kurzbz) { return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz' => $funktion_kurzbz)); } diff --git a/application/models/dashboard/Dashboard_Widget_model.php b/application/models/dashboard/Dashboard_Widget_model.php index eb4540cd9..9e0a4c200 100644 --- a/application/models/dashboard/Dashboard_Widget_model.php +++ b/application/models/dashboard/Dashboard_Widget_model.php @@ -12,5 +12,4 @@ class Dashboard_Widget_model extends DB_Model $this->pk = ['dashboard_id', 'widget_id']; $this->hasSequence = false; } - } diff --git a/application/models/dashboard/Widget_model.php b/application/models/dashboard/Widget_model.php index 554c7dea6..b1160e28f 100644 --- a/application/models/dashboard/Widget_model.php +++ b/application/models/dashboard/Widget_model.php @@ -29,5 +29,4 @@ class Widget_model extends DB_Model return $this->loadWhere(['dashboard_kurzbz' => $db]); } - } diff --git a/application/views/dashboard/dashboard_demo.php b/application/views/dashboard/dashboard_demo.php index 3518d769a..8efc230b7 100644 --- a/application/views/dashboard/dashboard_demo.php +++ b/application/views/dashboard/dashboard_demo.php @@ -1,5 +1,6 @@ load->view('templates/FHC-Header', +$this->load->view( + 'templates/FHC-Header', array( 'title' => 'FH-Complete', 'bootstrap5' => true, diff --git a/application/views/dashboard/dashboard_demo_admin.php b/application/views/dashboard/dashboard_demo_admin.php new file mode 100644 index 000000000..0d92146a8 --- /dev/null +++ b/application/views/dashboard/dashboard_demo_admin.php @@ -0,0 +1,32 @@ +load->view( + 'templates/FHC-Header', + array( + 'title' => 'FH-Complete', + 'bootstrap5' => true, + 'fontawesome6' => true, + 'axios027' => true, + 'restclient' => true, + 'vue3' => true, + 'customJSModules' => ['public/js/apps/DashboardAdmin.js'], + 'customCSSs' => [ + 'public/css/components/dashboard.css' + ], + 'navigationcomponent' => true + ) +); +?> + +
+ + + +
+
+

Dashboard

+
+ +
+
+ +load->view('templates/FHC-Footer'); ?> diff --git a/application/views/test/Admin.php b/application/views/test/Admin.php index 837f6411b..c5fa8b64c 100644 --- a/application/views/test/Admin.php +++ b/application/views/test/Admin.php @@ -1,5 +1,6 @@ load->view('templates/FHC-Header', +$this->load->view( + 'templates/FHC-Header', array( 'title' => 'FH-Complete', 'bootstrap5' => true, diff --git a/application/views/test/Test.php b/application/views/test/Test.php index d297bf4ec..328b45fba 100644 --- a/application/views/test/Test.php +++ b/application/views/test/Test.php @@ -1,5 +1,6 @@ load->view('templates/FHC-Header', +$this->load->view( + 'templates/FHC-Header', array( 'title' => 'FH-Complete', 'bootstrap5' => true, diff --git a/public/css/components/dashboard.css b/public/css/components/dashboard.css index b16f5f07b..b6ad81d2d 100644 --- a/public/css/components/dashboard.css +++ b/public/css/components/dashboard.css @@ -1,19 +1,25 @@ @import './calendar.css'; +.empty-tile-hover { + height: 100%; + width: 100%; + background-image: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + cursor:pointer; +} .alert-danger .form-check-input:checked { border-color: #842029; background-color: #842029; } -.draganddropcontainer { - grid-template-columns:repeat(4,1fr); - gap: 1rem; - place-items: stretch; - place-content: stretch; +:root { + --fhc-dashboard-grid-size: 4; } @media(max-width: 577px) { - .draganddropcontainer { - grid-template-columns:repeat(2,1fr); + :root { + --fhc-dashboard-grid-size: 2; } } @@ -26,4 +32,4 @@ } .cursor-move { cursor: move; -} \ No newline at end of file +} diff --git a/public/js/apps/Dashboard.js b/public/js/apps/Dashboard.js index 54a73441c..e56425a6d 100644 --- a/public/js/apps/Dashboard.js +++ b/public/js/apps/Dashboard.js @@ -8,9 +8,5 @@ Vue.createApp({ components: { CoreNavigationCmpt, CoreDashboard - /*, - "CoreFilterCmpt": CoreFilterCmpt, - "verticalsplit": verticalsplit, - "searchbar": searchbar*/ } }).mount('#main'); diff --git a/public/js/apps/Test.js b/public/js/apps/DashboardAdmin.js similarity index 58% rename from public/js/apps/Test.js rename to public/js/apps/DashboardAdmin.js index cdf3e78f3..fed16f577 100644 --- a/public/js/apps/Test.js +++ b/public/js/apps/DashboardAdmin.js @@ -1,5 +1,4 @@ import {CoreNavigationCmpt} from '../components/navigation/Navigation.js'; -import CoreDashboard from '../components/Dashboard/Dashboard.js'; import DashboardAdmin from '../components/Dashboard/Admin.js'; Vue.createApp({ @@ -8,11 +7,7 @@ Vue.createApp({ }), components: { CoreNavigationCmpt, - DashboardAdmin, - CoreDashboard/*, - "CoreFilterCmpt": CoreFilterCmpt, - "verticalsplit": verticalsplit, - "searchbar": searchbar*/ + DashboardAdmin }, mounted() { } diff --git a/public/js/components/Dashboard/Admin.js b/public/js/components/Dashboard/Admin.js index 775cd87d6..b92609ad7 100644 --- a/public/js/components/Dashboard/Admin.js +++ b/public/js/components/Dashboard/Admin.js @@ -35,7 +35,7 @@ export default { BsPrompt.popup('New Dashboard name').then( name => { _name = name; - return axios.post(this.apiurl + '/Dashboard/Create', { + return axios.post(this.apiurl + '/Dashboard/create', { dashboard_kurzbz: name }) } @@ -51,14 +51,14 @@ export default { }, dashboardUpdate(dashboard) { // TODO(chris): Loading or message - axios.post(this.apiurl + '/Dashboard/Update', dashboard).then(() => { + axios.post(this.apiurl + '/Dashboard/update', dashboard).then(() => { let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id); old.dashboard_kurzbz = dashboard.dashboard_kurzbz; old.beschreibung = dashboard.beschreibung; }).catch(err => console.error('ERROR:', err)); }, dashboardDelete(dashboard_id) { - axios.post(this.apiurl + '/Dashboard/Delete', {dashboard_id}).then(() => { + axios.post(this.apiurl + '/Dashboard/delete', {dashboard_id}).then(() => { this.current = -1; this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id); }).catch(err => console.error('ERROR:', err)); diff --git a/public/js/components/Dashboard/Admin/Presets.js b/public/js/components/Dashboard/Admin/Presets.js index a86de1966..63e010014 100644 --- a/public/js/components/Dashboard/Admin/Presets.js +++ b/public/js/components/Dashboard/Admin/Presets.js @@ -29,6 +29,7 @@ export default { this.$refs.widgetpicker.getWidget().then(widget_id => { widget.widget = widget_id; delete widget.custom; + widget.preset = 1; let loading = {...widget}; loading.loading = true; this.sections.forEach(section => { @@ -119,7 +120,7 @@ export default { let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value); this.sections = []; this.tmpLoading = funktionen.join('###'); - axios.get(this.apiurl + '/Config/PresetBatch', {params: { + axios.get(this.apiurl + '/Config/presetBatch', {params: { db: this.dashboard, funktionen }}).then(res => { @@ -142,7 +143,7 @@ export default { } }, created() { - axios.get(this.apiurl + '/Config/Funktionen').then(res => { + axios.get(this.apiurl + '/Config/funktionen').then(res => { //console.log(res.data.retval); this.funktionen = {general: 'GENERAL'}; res.data.retval.forEach(funktion => { diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js index 2f2aa4d79..c87ad9b65 100644 --- a/public/js/components/Dashboard/Dashboard.js +++ b/public/js/components/Dashboard/Dashboard.js @@ -70,7 +70,7 @@ export default { if (this.sections[i].widgets[wid].id == k) { payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]); // NOTE(chris): remove internal props - for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1}) + for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1,preset:1}) if (payload[k][prop]) delete payload[k][prop]; break; @@ -126,14 +126,19 @@ export default { //console.log(res.data.retval); for (var name in res.data.retval.widgets) { let widgets = []; + let remove = []; for (var wid in res.data.retval.widgets[name]) { res.data.retval.widgets[name][wid].id = wid; - widgets.push(res.data.retval.widgets[name][wid]); + if (res.data.retval.widgets[name][wid].custom || res.data.retval.widgets[name][wid].preset) + widgets.push(res.data.retval.widgets[name][wid]); + else + remove.push(wid); } this.sections.push({ name: name, widgets: widgets }); + remove.forEach(wid => this.widgetRemove(name, wid)); } }).catch(err => console.error('ERROR:', err)); }, diff --git a/public/js/components/Dashboard/Item.js b/public/js/components/Dashboard/Item.js index d64b8d2e7..43e03ebc7 100644 --- a/public/js/components/Dashboard/Item.js +++ b/public/js/components/Dashboard/Item.js @@ -97,14 +97,15 @@ export default { this.arguments = {...this.widget.arguments, ...this.config}; this.tmpConfig = {...this.arguments}; }, - template: `
+ template: ` +
-
+
- + {{ widget.setup.name }} @@ -114,7 +115,7 @@ export default {
-
+
@@ -132,7 +133,7 @@ export default {
` } \ No newline at end of file diff --git a/public/js/components/Dashboard/Section.js b/public/js/components/Dashboard/Section.js index 84bcdd58e..868672a50 100644 --- a/public/js/components/Dashboard/Section.js +++ b/public/js/components/Dashboard/Section.js @@ -1,14 +1,16 @@ import BsConfirm from "../Bootstrap/Confirm.js"; +import DropGrid from '../Drop/Grid.js' import DashboardItem from "./Item.js"; import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; -// TODO(chris): handle overflow (moving outside the box) export default { components: { + DropGrid, DashboardItem }, inject: { adminMode: { + type: Boolean, default: false } }, @@ -23,371 +25,43 @@ export default { ], data() { return { - gridWidth: 0, - changeHeight: 1, - movedObjects: [], - editMode: this.adminMode ? 1 : 0, - gridXLast: -1, // NOTE(chris): 0 based - gridYLast: -1, - dragging: 0, - dataTransfer: {}, - gridAddFound: false, - gridXAdd: -1, // NOTE(chris): 0 based - gridYAdd: -1 + gridWidth: 1, + editMode: this.adminMode } }, computed: { items() { - this.widgets.forEach((item,i) => item.index = i); - return this.widgets; - }, - itemCoords() { // NOTE(chris): 1 based - if (!this.gridWidth) - return []; - let itemCoords = this.items.map(item => item.place[this.gridWidth] || this.createItemPlacement(item)); - // TODO(chris): verify positions & sizes - let occupiers = []; - let wrongPlacedItems = []; - let gridWidth = this.gridWidth; - this.items.forEach(item => { - let x = item._x !== undefined ? item._x : itemCoords[item.index].x; - let y = item._y !== undefined ? item._y : itemCoords[item.index].y; - let w = item._w !== undefined ? item._w : itemCoords[item.index].w; - let h = item._h !== undefined ? item._h : itemCoords[item.index].h; - // TODO(chris): check with and height params here? - for (var i = 0; i < w; i++) { - for (var j = 0; j < h; j++) { - var c = (y+j-1) * gridWidth + (x+i-1); - // NOTE(chris): check for overlaping items - if (occupiers[c] !== undefined) { - //console.log('try to add ' + item.index + ' to ' + x + '/' + y + ', but ' + occupiers[c] + ' is already there'); - // NOTE(chris): remove possible other entries of this item - for (var c2 = c; c2; c2--) - if (occupiers[c2] == item.index) - occupiers[c2] = undefined; - wrongPlacedItems.push(item); - return; - } - occupiers[c] = item.index; - } - } + return this.widgets.map(item => { + return {...item, ...(item.place[this.gridWidth] || {})}; }); - wrongPlacedItems.forEach(item => { - let w = item._w !== undefined ? item._w : itemCoords[item.index].w; - let h = item._h !== undefined ? item._h : itemCoords[item.index].h; - for (var c = 0; c < occupiers.length + gridWidth; c++) { - if (occupiers[c] === undefined) { - var occupied = false, i, j; - for (i = 0; i < w; i++) { - for (j = 0; j < h; j++) { - if (occupiers[c + i + j * gridWidth] !== undefined) { - i = w; - occupied = true; - break; - } - } - } - if (!occupied) { - item.place[gridWidth].x = c%gridWidth + 1; - item.place[gridWidth].y = Math.floor(c/gridWidth) + 1; - for (i = 0; i < w; i++) { - for (j = 0; j < h; j++) { - occupiers[c + i + j * gridWidth] = item.index; - } - } - return; - } - } - } - }); - return itemCoords; - }, - gridHeight() { - if (!this.gridWidth || !this.changeHeight) - return 0; - let minH = 0; - this.itemCoords.forEach((item,i) => minH = Math.max(minH, (!this.editMode && this.items[i].hidden) ? 0 : item.y - 1 + item.h)); - // TODO(chris): the extraline should only be present if all slots are occupied - if (minH == 0 && this.editMode) - return 1; - return minH + this.editMode*this.dragging; - }, - gridOccupiers() { // NOTE(chris): 0 based - let occupiers = []; - let gridWidth = this.gridWidth; - this.items.forEach(item => { - let x = item._x !== undefined ? item._x : this.itemCoords[item.index].x; - let y = item._y !== undefined ? item._y : this.itemCoords[item.index].y; - let w = item._w !== undefined ? item._w : this.itemCoords[item.index].w; - let h = item._h !== undefined ? item._h : this.itemCoords[item.index].h; - for (var i = 0; i < w; i++) { - for (var j = 0; j < h; j++) { - var c = (y+j-1) * gridWidth + (x+i-1); - occupiers[c] = item.index; - } - } - }); - return occupiers; - }, - cssBg() { - if (!this.editMode || this.dragging || !this.gridAddFound) - return 'transparent'; - let x = this.gridXAdd, y = this.gridYAdd, h = this.gridHeight-1 || 1, w = this.gridWidth-1 || 1; - return `url('data:image/svg+xml;utf8,')` + (100 * x/w) + '% ' + (100 * y/h) + '%/' + (100/this.gridWidth) + '% ' + (100/this.gridHeight) + '% no-repeat;cursor:pointer'; } }, methods: { - addWidget(evt) { - if (evt.target != this.$refs.container || !this.editMode) - return; - const rect = this.$refs.container.getBoundingClientRect(); - const gridX = Math.floor(this.gridWidth * (evt.clientX - rect.left) / this.$refs.container.clientWidth); - const gridY = Math.floor(this.gridHeight * (evt.clientY - rect.top) / this.$refs.container.clientHeight); - if (this.gridOccupiers[gridY * this.gridWidth + gridX] === undefined) { - let widget = { widget: 1, config: {}, place: {}, custom: 1 }; - widget.place[this.gridWidth] = { - x: gridX+1, - y: gridY+1, - w: 1, - h: 1 - }; - this.$emit('widgetAdd', this.name, widget); + checkResizeLimit(item, w, h) { + // NOTE(chris): widgets needs to be loaded for this to work + let widget = CachedWidgetLoader.getWidget(item.widget); + if (widget) { + let minmaxW = widget.setup.width; + if (minmaxW.max) + minmaxW.min = minmaxW.min || 1; + else + minmaxW = {min:minmaxW,max:minmaxW}; + if (w < minmaxW.min) + w = minmaxW.min; + if (w > minmaxW.max) + w = minmaxW.max; + + let minmaxH = widget.setup.height; + if (minmaxH.max) + minmaxH.min = minmaxH.min || 1; + else + minmaxH = {min:minmaxH,max:minmaxH}; + if (h < minmaxH.min) + h = minmaxH.min; + if (h > minmaxH.max) + h = minmaxH.max; } - }, - createItemPlacement(item) { - // TODO(chris): create correct default placement if it is not there - item.place[this.gridWidth] = {x:1,y:1,w:1,h:1}; - /*var freeList = [], nextId = 0; - this.items.forEach(item => { - if (!item.place[this.gridWidth]) { - if (!this.gridWidth) { - item.place[this.gridWidth] = {x:1,y:nextId++,w:1,h:1}; - } else { - // TODO(chris): IMPLEMENT widths & heights - if (freeList[nextId]) - while (freeList[++nextId]); - freeList[nextId] = 1; - item.place[this.gridWidth] = {x:(nextId%this.gridWidth)+1,y:Math.floor(nextId/this.gridWidth)+1,w:1,h:1}; - } - } - });*/ - return item.place[this.gridWidth]; - }, - onMouseMove(evt) { - if (!this.editMode || this.dragging) { - this.gridXAdd = this.gridYAdd = -1; - return; - } - - const rect = this.$refs.container.getBoundingClientRect(); - const gridX = Math.floor(this.gridWidth * (evt.clientX - rect.left) / this.$refs.container.clientWidth); - const gridY = Math.floor(this.gridHeight * (evt.clientY - rect.top) / this.$refs.container.clientHeight); - - if (this.gridXAdd == gridX && this.gridYAdd == gridY) - return; - // TODO(chris): only mark it when its not occupied - this.gridXAdd = gridX; - this.gridYAdd = gridY; - this.gridAddFound = (this.gridOccupiers[gridX + gridY * this.gridWidth] === undefined); - }, - onMouseLeave() { - this.gridXAdd = this.gridYAdd = -1; - this.gridAddFound = false; - }, - startDrag(evt, item) { - this.dragging = 1; - this.gridXLast = -1; - this.gridYLast = -1; - item._x = this.itemCoords[item.index].x; - item._y = this.itemCoords[item.index].y; - - evt.dataTransfer.dropEffect = 'move'; - evt.dataTransfer.effectAllowed = 'move'; - this.dataTransfer = { - action: 'm', - id: item.index, - w: this.itemCoords[item.index].w, - h: this.itemCoords[item.index].h - } - }, - startResize(evt, item) { - this.dragging = 1; - this.gridXLast = -1; - this.gridYLast = -1; - item._w = this.itemCoords[item.index].w; - item._h = this.itemCoords[item.index].h; - - evt.dataTransfer.setDragImage(evt.target, -99999, -99999); - evt.dataTransfer.dropEffect = 'move'; - evt.dataTransfer.effectAllowed = 'move'; - this.dataTransfer = { - action: 'r', - id: item.index, - x: this.itemCoords[item.index].x, - y: this.itemCoords[item.index].y - } - }, - occupyFields(id, x, y, w, h) { - var c; - while ((c = this.movedObjects.pop())) { - if (this.items[c]._y !== undefined) { - this.items[c].place[this.gridWidth].y = this.items[c]._y; - this.items[c]._y = undefined; - } - } - var move = {}; - move[id] = this.items[id]; - this.getOccupiedItems(x,y,w,h,move); - h = y + h; - y = 0; - for (x in move) { - if (x != id) { - c = move[x]._y !== undefined ? move[x]._y : this.itemCoords[x].y; - if (c < h) - y = Math.max(h-c, y); - } - } - for (x in move) { - if (x != id) { - this.movedObjects.push(x); - if (move[x]._y === undefined) { - move[x]._y = this.itemCoords[x].y; - } - move[x].place[this.gridWidth].y = move[x]._y + y; - } - } - }, - getOccupiedItems(x, y, w, h, move) { - var i, j, c; - for (i = 0; i < w; i++) { - for (j = 0; j < h; j++) { - c = (y+j-1) * this.gridWidth + (x+i-1); - if (this.gridOccupiers[c] !== undefined && !move[this.gridOccupiers[c]]) { - move[this.gridOccupiers[c]] = this.items[this.gridOccupiers[c]]; - c = this.itemCoords[this.gridOccupiers[c]]; - this.getOccupiedItems(c.x, c.y + 1, c.w, c.h, move); - } - } - } - }, - onDragOver(evt) { - let id, x, y, w, h; - const action = this.dataTransfer.action; - const rect = this.$refs.container.getBoundingClientRect(); - const gridX = Math.floor(this.gridWidth * (evt.clientX - rect.left) / this.$refs.container.clientWidth); - const gridY = Math.floor(this.gridHeight * (evt.clientY - rect.top) / this.$refs.container.clientHeight); - - if (this.gridXLast == gridX && this.gridYLast == gridY) - return; - this.gridXLast = gridX; - this.gridYLast = gridY; - - if (action == 'm') { - x = Math.max(gridX + 1, 1); - y = Math.max(gridY + 1, 1); - w = parseInt(this.dataTransfer.w); - h = parseInt(this.dataTransfer.h); - - if (x + w > this.gridWidth + 1) - x = this.gridWidth + 1 - w; - - id = this.dataTransfer.id; - this.occupyFields(id, x, y, w, h); - - this.itemCoords[id].x = x; - this.itemCoords[id].y = y; - } else if (action == 'r') { - x = parseInt(this.dataTransfer.x); - y = parseInt(this.dataTransfer.y); - w = gridX + 2 - x; - h = gridY + 2 - y; - w = Math.max(1, w); - h = Math.max(1, h); - - if (x + w > this.gridWidth + 1) - w = this.gridWidth + 1 - x; - - id = this.dataTransfer.id; - let widget = CachedWidgetLoader.getWidget(this.items[id].widget); - if (widget) { - let minmaxW = widget.setup.width; - if (minmaxW.max) - minmaxW.min = minmaxW.min || 1; - else - minmaxW = {min:minmaxW,max:minmaxW}; - if (w < minmaxW.min) - w = minmaxW.min; - if (w > minmaxW.max) - w = minmaxW.max; - - let minmaxH = widget.setup.height; - if (minmaxH.max) - minmaxH.min = minmaxH.min || 1; - else - minmaxH = {min:minmaxH,max:minmaxH}; - if (h < minmaxH.min) - h = minmaxH.min; - if (h > minmaxH.max) - h = minmaxH.max; - } - - this.occupyFields(id, x, y, w, h); - - this.itemCoords[id].w = w; - this.itemCoords[id].h = h; - } - }, - onDrop() { - this.dragging = 0; - this.gridXLast = -1; - this.gridYLast = -1; - let id = 0; - let update = {}; - while ((id = this.movedObjects.pop())) { - if (this.items[id]._y !== undefined) { - if (this.itemCoords[id].y != this.items[id]._y) { - update[this.items[id].id] = {place:{}}; - update[this.items[id].id].place[this.gridWidth] = {y:this.itemCoords[id].y}; - } - this.items[id]._y = undefined; - } - } - - id = this.dataTransfer.id; - - const action = this.dataTransfer.action; - update[this.items[id].id] = {place:{}}; - update[this.items[id].id].place[this.gridWidth] = {}; - - if (action == 'm') { - if (this.items[id]._x !== undefined) { - if (this.itemCoords[id].x != this.items[id]._x) { - update[this.items[id].id].place[this.gridWidth].x = this.itemCoords[id].x; - } - this.items[id]._x = undefined; - } - if (this.items[id]._y !== undefined) { - if (this.itemCoords[id].y != this.items[id]._y) { - update[this.items[id].id].place[this.gridWidth].y = this.itemCoords[id].y; - } - this.items[id]._y = undefined; - } - } else if (action == 'r') { - update[this.items[id].id].place[this.gridWidth].w = this.itemCoords[id].w; - update[this.items[id].id].place[this.gridWidth].h = this.itemCoords[id].h; - } - - if (update[this.items[id].id].place[this.gridWidth].x === undefined && - update[this.items[id].id].place[this.gridWidth].y === undefined && - update[this.items[id].id].place[this.gridWidth].w === undefined && - update[this.items[id].id].place[this.gridWidth].h === undefined) { - delete update[this.items[id].id].place[this.gridWidth]; - } - - this.updatePreset(update); - - // TODO(chris): find better way to trigger change for gridHeight - this.changeHeight++ + return [w, h]; }, removeWidget(item, revert) { if (item.custom) { @@ -400,9 +74,32 @@ export default { }, saveConfig(config, item) { let payload = {}; - payload[item.id] = { config };console.log(payload); + payload[item.id] = { config }; this.updatePreset(payload); }, + updatePositions(updated) { + let result = {}; + updated.forEach(update => { + let item = {...update.item}; + if (!item.place[this.gridWidth]) + item.place[this.gridWidth] = {x: 0, y: 0, w: 1, h: 1}; + delete item.x; + delete item.y; + delete item.w; + delete item.h; + if (update.x !== undefined) + item.place[this.gridWidth].x = update.x; + if (update.y !== undefined) + item.place[this.gridWidth].y = update.y; + if (update.w !== undefined) + item.place[this.gridWidth].w = update.w; + if (update.h !== undefined) + item.place[this.gridWidth].h = update.h; + + result[item.id] = item; + }); + this.updatePreset(result); + }, updatePreset(update) { let payload = {}; payload[this.name] = update; @@ -412,55 +109,34 @@ export default { mounted() { let self = this; let cont = self.$refs.container; - self.gridWidth = window.getComputedStyle(cont).getPropertyValue('grid-template-columns').split(" ").length; + self.gridWidth = parseInt(window.getComputedStyle(cont).getPropertyValue('--fhc-dashboard-grid-size')); window.addEventListener('resize', () => { - for (const child of cont.children) { - child.style.display = 'none'; - } - self.gridWidth = window.getComputedStyle(cont).getPropertyValue('grid-template-columns').split(" ").length; - for (const child of cont.children) { - child.style.display = ''; - } + self.gridWidth = parseInt(window.getComputedStyle(cont).getPropertyValue('--fhc-dashboard-grid-size')); }); }, - template: `
+ template: ` +

{{name}} - +

-
-
- - + + +
` -} \ No newline at end of file +} diff --git a/public/js/components/Drop/Grid.js b/public/js/components/Drop/Grid.js new file mode 100644 index 000000000..62e4c15ea --- /dev/null +++ b/public/js/components/Drop/Grid.js @@ -0,0 +1,377 @@ +// TODO(chris): Comments + +import GridItem from './Grid/Item.js'; +import GridLogic from '../../composables/GridLogic.js'; + +const MODE_IDLE = 0; +const MODE_MOVE = 1; +const MODE_RESIZE = 2; + +export default { + components: { + GridItem + }, + inject: { + }, + props: { + cols: Number, + items: Array, + resizeLimit: Function, + active: { + type: Boolean, + default: true + }, + marginForExtraRow: { + type: Number, + default: 0 + } + }, + emits: [ + "rearrangeItems", + "newItem" + ], + data() { + return { + x: -1, + y: -1, + mode: MODE_IDLE, + grid: null, + dragGrid: null, + permUpdates: [], + positionUpdates: null, + fixedPositionUpdates: null, + draggedOffset: [0,0], + draggedItem: null, + additionalRow: null + } + }, + computed: { + rows() { + if ((this.mode == MODE_MOVE || this.mode == MODE_RESIZE) && this.dragGrid) + return this.dragGrid.h; + return this.grid ? this.grid.h : 1; + }, + gridStyle() { + const addH = this.active ? this.marginForExtraRow : 0; + return { + '--fhc-dg-row-height': 100/(this.rows + addH) + '%', + '--fhc-dg-col-width': 100/this.cols + '%', + 'padding-bottom': 100 * (this.rows + addH)/this.cols + '%' + } + }, + indexedItems() { + return this.items.map( + (item, index) => { + return { + index: index, + x: item.x, + y: item.y, + w: item.w, + h: item.h, + weight: item.weight || 0, + data: item + } + } + ); + }, + prePlacedItems() { + if (!this.fixedPositionUpdates) + return this.indexedItems; + return this.indexedItems.map(item => { + if (!this.fixedPositionUpdates[item.index]) + return item; + return { + index: item.index, + weight: item.weight, + data: item.data, + x: this.fixedPositionUpdates[item.index].x === undefined ? item.x : this.fixedPositionUpdates[item.index].x, + y: this.fixedPositionUpdates[item.index].y === undefined ? item.y : this.fixedPositionUpdates[item.index].y, + w: this.fixedPositionUpdates[item.index].w === undefined ? item.w : this.fixedPositionUpdates[item.index].w, + h: this.fixedPositionUpdates[item.index].h === undefined ? item.h : this.fixedPositionUpdates[item.index].h + }; + }); + }, + placedItems() { + if (!this.positionUpdates) + return this.prePlacedItems; + return this.prePlacedItems.map(item => { + if (!this.positionUpdates[item.index]) + return item; + return { + index: item.index, + weight: item.weight, + data: item.data, + x: this.positionUpdates[item.index].x === undefined ? item.x : this.positionUpdates[item.index].x, + y: this.positionUpdates[item.index].y === undefined ? item.y : this.positionUpdates[item.index].y, + w: this.positionUpdates[item.index].w === undefined ? item.w : this.positionUpdates[item.index].w, + h: this.positionUpdates[item.index].h === undefined ? item.h : this.positionUpdates[item.index].h + }; + }); + }, + showEmptyTileHover() { + if (!this.active || !this.grid || this.mode != MODE_IDLE || this.x < 0 || this.y < 0 || this.x >= this.cols || this.y >= this.rows) + return false; + return this.grid.isFreeSlot(this.x, this.y); + } + }, + watch: { + active(active) { + if (!active) + this.dragCancel(); + }, + cols() { + this.dragCancel(); + }, + indexedItems: { + handler(value) { + this.dragCancel(); + + const updated = this.createNewGrid(value); + + this.fixedPositionUpdates = updated; + if (updated.length) + this.$emit('rearrangeItems', updated.filter(v => v)); + }, + immediate: true, + deep: true + } + }, + methods: { + createNewGrid(items) { + this.grid = new GridLogic(this.cols); + const result = []; + [...items].sort((a, b) => a.weight > b.weight).forEach(item => { + if (item.x + item.w > this.cols) { + let targetW = this.cols-item.x, + targetX = undefined; + if (this.resizeLimit) { + [targetW] = this.resizeLimit(item.data, targetW, item.h); + } + if (targetW < 1) + targetW = 1; + if (targetW > this.cols) + targetW = this.cols; + if (item.x + targetW > this.cols) { + targetX = this.cols - targetW; + } + if (targetW == item.w) + targetW = undefined; + result[item.index] = { + item: item.data, + x: targetX, + w: targetW + }; + } + item.frame = this.grid.getItemFrame(item); + this.convertGridResultToUpdate(this.grid.add(item), result, items); + }); + this.grid.clearWeights(); + return result; + }, + convertGridResultToUpdate(input, output, baseArray) { + if (!input) + return; + if (!baseArray) + baseArray = this.indexedItems; + input.forEach(item => { + let result = { + item: baseArray[item.index].data + }; + if (item.x !== undefined) + result.x = item.x; + if (item.y !== undefined) + result.y = item.y; + if (item.w !== undefined) + result.w = item.w; + if (item.h !== undefined) + result.h = item.h; + output[item.index] = result; + }); + }, + mouseLeave() { + if (this.mode == MODE_IDLE) { + this.x = -1; + this.y = -1; + if (this.additionalRow !== null) { + this.grid.h = this.additionalRow; + this.additionalRow = null; + } + } + }, + updateCursor(evt) { + if (!this.active) { + this.x = this.y = -1; + return false; + } + const addH = this.active ? this.marginForExtraRow : 0; + const rect = this.$refs.container.getBoundingClientRect(); + + if (!evt.clientX && !evt.clientY && evt.touches) { + evt.clientX = evt.touches[0].clientX; + evt.clientY = evt.touches[0].clientY; + } + + const gridX = Math.floor(this.cols * (evt.clientX - rect.left) / this.$refs.container.clientWidth); + const gridY = Math.floor((this.rows + addH) * (evt.clientY - rect.top) / this.$refs.container.clientHeight); + if (this.x == gridX && this.y == gridY) + return false; + + if (this.mode == MODE_IDLE) { + if (this.additionalRow === null && this.y == this.rows-1 && gridY == this.rows) { + this.additionalRow = this.grid.h; + this.grid.h += 1; + } else if (this.additionalRow !== null && gridY != this.rows - 1) { + this.grid.h = this.additionalRow; + this.additionalRow = null; + } + } + + this.x = gridX; + this.y = gridY; + + return true; + }, + _dragStart(evt) { + if (evt.dataTransfer) { + evt.dataTransfer.setDragImage(evt.target, -99999, -99999); + evt.dataTransfer.dropEffect = 'move'; + evt.dataTransfer.effectAllowed = 'move'; + } + }, + startMove(evt, item) { + if (!this.active) + return; + this._dragStart(evt); + this.mode = MODE_MOVE; + this.updateCursor(evt); + this.draggedItem = item; + this.draggedOffset = [item.x - this.x, item.y - this.y]; + }, + startResize(evt, item) { + if (!this.active) + return; + this._dragStart(evt); + this.mode = MODE_RESIZE; + this.draggedItem = item; + }, + dragOver(evt) { + if (!this.active) + return this.dragCancel(); + if (this.updateCursor(evt)) { + switch(this.mode) { + case MODE_MOVE: { + evt.preventDefault(); + this.dragGrid = new GridLogic(this.grid); + let x = this.x + this.draggedOffset[0]; + let y = this.y + this.draggedOffset[1]; + if (x < 0) { + this.draggedOffset[0] -= x; + x = 0; + } else if (x + this.draggedItem.w > this.cols) { + this.draggedOffset[0] += this.cols - this.draggedItem.w - x; + x = this.cols - this.draggedItem.w; + } + if (y < 0) { + this.draggedOffset[1] -= y; + y = 0; + } + + this.positionUpdates = this.dragGrid.move(this.draggedItem, x, y); + break; + } + case MODE_RESIZE: { + evt.preventDefault(); + this.dragGrid = new GridLogic(this.grid); + let w = Math.min(this.cols - this.draggedItem.x, Math.max(1, this.x - this.draggedItem.x + 1)); + let h = Math.max(1, this.y - this.draggedItem.y + 1); + if (this.resizeLimit) + [w, h] = this.resizeLimit(this.draggedItem.data, w, h); + this.positionUpdates = this.dragGrid.resize(this.draggedItem, w, h); + break; + } + } + } + }, + dragCancel() { + this.mode = MODE_IDLE; + this.positionUpdates = null; + this.draggedOffset = [0,0], + this.draggedItem = null; + }, + dragEnd() { + if (this.mode == MODE_IDLE) + return; + if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols) + return this.dragCancel(); + this.mode = MODE_IDLE; + let updated = []; + this.convertGridResultToUpdate(this.positionUpdates, updated); + updated = this._updateFixedPositions(updated); + if (updated.length) + this.$emit('rearrangeItems', updated.filter(v => v)); + }, + _updateFixedPositions(updated) { + updated.forEach((item, index) => { + if (!this.fixedPositionUpdates[index]) + this.fixedPositionUpdates[index] = item; + else + this.fixedPositionUpdates[index] = {...this.fixedPositionUpdates[index], ...item}; + }); + let additionalUpdates = this.createNewGrid(this.prePlacedItems); + if (additionalUpdates.length) { + // NOTE(chris): this should never happen but it's here for safety + additionalUpdates.forEach((item, index) => updated[index] = item); + return this._updateFixedPositions(updated); + } + return updated; + }, + emptyTileClicked() { + this.$emit('newItem', this.x, this.y); + } + }, + template: ` + ` +} diff --git a/public/js/components/Drop/Grid/Item.js b/public/js/components/Drop/Grid/Item.js new file mode 100644 index 000000000..47eec85ee --- /dev/null +++ b/public/js/components/Drop/Grid/Item.js @@ -0,0 +1,71 @@ +export default { + components: { + }, + inject: { + }, + props: { + item: Object, + active: Boolean + }, + emits: [ + "startMove", + "startResize", + "endDrag", + "dropDrag" + ], + data() { + return { + dragAction: '', + dragging: false + } + }, + computed: { + }, + methods: { + registerDragAction(evt) { + if (evt.target.hasAttribute('drag-action')) { + this.dragAction = evt.target.getAttribute('drag-action'); + } else { + let parent = evt.target.closest('[drag-action]'); + if (parent) { + this.dragAction = parent.getAttribute('drag-action'); + } else { + this.dragAction = ''; + } + } + }, + tryDragStart(evt, item) { + let dragAction = this.dragAction || evt.target.getAttribute('drag-action'); + if (dragAction) { + this.dragging = true; + if (dragAction == 'move') + return this.$emit('startMove', evt, item); + else if (dragAction == 'resize') + return this.$emit('startResize', evt, item); + } + evt.preventDefault(); + }, + touchDragEnd(evt) { + if (!this.dragging) + return evt.preventDefault(); + this.dragging = false; + this.$emit('dropDrag', evt); + }, + test(evt) { + let dragAction = this.dragAction || evt.target.getAttribute('drag-action'); + if (dragAction) { + this.dragging = true; + } + } + }, + template: ` +
+ +
` +} diff --git a/public/js/composables/GridLogic.js b/public/js/composables/GridLogic.js new file mode 100644 index 000000000..10aa2909c --- /dev/null +++ b/public/js/composables/GridLogic.js @@ -0,0 +1,208 @@ +// TODO(chris): Comments + +const DIR_UP = 0; +const DIR_LEFT = 1; +const DIR_RIGHT = 2; +const DIR_DOWN = 3; + +class GridLogic { + constructor(w) { + if (w.w) { + this.w = w.w; + this.h = w.h; + this.data = [...w.data]; + this.grid = [...w.grid]; + } else { + this.w = w; + this.h = 1; + this.data = []; + this.grid = []; + } + } + isFreeSlot(x, y) { + const i = y*this.w + x; + return !this.grid[i] && this.grid[i] !== 0; + } + add(item, prefer) { + let occupiers = this.getItemsInFrame(item.frame); + + if (!occupiers.length) { + item.frame.forEach(f => this.grid[f] = item.index); + this.data[item.index] = item; + this.h = Math.max(this.h, item.y + item.h); + return []; + } else { + const intermGrid = new GridLogic(this); + + item.frame.forEach(f => intermGrid.grid[f] = -1); + + const possiblities = intermGrid.tryMoving(occupiers, prefer); + if (possiblities.length) { + const bestOption = possiblities.sort((a,b) => { + if (a.cost === undefined) + a.cost = a.moves.reduce((a, v) => a * v.cost, 1); + if (b.cost === undefined) + b.cost = b.moves.reduce((a, v) => a * v.cost, 1); + return a.cost > b.cost; + }); + const result = []; + bestOption[0].moves.forEach(move => { + const currItem = {...this.data[move.index]}; + currItem.x += move.x; + currItem.y += move.y; + currItem.frame = this.getItemFrame(currItem); + this.h = Math.max(this.h, currItem.y + currItem.h); + this.remove(currItem); + currItem.frame.forEach(f => this.grid[f] = currItem.index); + this.data[move.index] = currItem; + result[move.index] = { + index: currItem.index, + x: currItem.x, + y: currItem.y + }; + }); + item.frame.forEach(f => this.grid[f] = item.index); + this.data[item.index] = item; + return result; + } else { + console.error('FATAL', "can't arrange item on grid"); + } + } + } + move(item, x, y) { + if (item.x == x && item.y == y) + return []; + this.remove(item); + + let prefer = undefined; + if (item.x == x) { + if (y-item.y > 0) + prefer = DIR_UP; + else + prefer = DIR_DOWN; + } else if (item.y == y) { + if (x-item.x > 0) + prefer = DIR_LEFT; + else + prefer = DIR_RIGHT; + } + + const currItem = {...item}; + currItem.x = x; + currItem.y = y; + currItem.frame = this.getItemFrame(currItem); + + const updates = this.add(currItem, prefer); + updates[item.index] = {index: item.index, x, y}; + return updates; + } + resize(item, w, h) { + if (item.w == w && item.h == h) + return []; + this.remove(item); + + const currItem = {...item}; + currItem.w = w; + currItem.h = h; + currItem.frame = this.getItemFrame(currItem); + + const updates = this.add(currItem); + updates[item.index] = {index: item.index, w, h}; + return updates; + } + tryMoving(index, prefer) { + if (Array.isArray(index)) { + index.forEach(i => this.remove({index:i})); + let possiblities = [{grid: this, moves: []}]; + index.forEach(i => { + let newPoss = []; + possiblities.forEach(possiblity => { + possiblity.grid.tryMoving(i, prefer).forEach(p => { + possiblity.moves + p.moves = [...p.moves, ...possiblity.moves]; + newPoss.push(p) + }); + }); + possiblities = newPoss; + }); + return possiblities; + } + const directions = [DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT]; + + this.remove({index}); + + const weight = 1 + .2*(this.data[index].weight || 0); + + return directions.reduce((result, dir) => { + let res = this.tryMovingInDirection(dir, index, 1, (prefer === dir ? .5 : 1 + dir*.1) * weight); + if (!res) + return result; + return [...result, ...res]; + }, []).filter(p => p); + } + tryMovingInDirection(dir, index, amount, cost) { + const move = {index, x:0, y: 0, cost: cost}; + let targetframe; + switch(dir) { + case DIR_UP: + if (this.data[index].y - amount < 0) + return false; + targetframe = this.data[index].frame.map(i => i-this.w*amount); + move.y = -amount; + break; + case DIR_DOWN: + if (this.data[index].y + this.data[index].h + amount > this.h) + cost += .4; + targetframe = this.data[index].frame.map(i => i+this.w*amount); + move.y = amount; + break; + case DIR_LEFT: + if (this.data[index].x - amount < 0) + return false; + targetframe = this.data[index].frame.map(i => i-amount); + move.x = -amount; + break; + case DIR_RIGHT: + if (this.data[index].x + this.data[index].w + amount > this.w) + return false; + targetframe = this.data[index].frame.map(i => i+amount); + move.x = amount; + break; + } + + const occupiers = this.getItemsInFrame(targetframe); + if (occupiers.includes(-1)) { + return this.tryMovingInDirection(dir, index, amount+1, cost); + } + + const intermGrid = new GridLogic(this); + targetframe.forEach(f => intermGrid.grid[f] = -1); + + if (!occupiers.length) { + return [{grid: intermGrid, moves: [move]}]; + } + const possiblities = intermGrid.tryMoving(occupiers).map(possiblity => possiblity.moves.unshift(move) && possiblity); + return possiblities.length ? possiblities : false; + } + clearWeights() { + this.data.forEach(item => item.weight = undefined); + } + getItemsInFrame(frame) { + return frame.map(i => this.grid[i]).filter((v,i,a) => (v || v === 0) && a.indexOf(v) === i); + } + remove(item) { + this.grid = this.grid.map(i => i != item.index ? i : undefined); + } + getItemFrame(item) { + const frame = []; + for (let i = 0; i < item.w; i++) + for (let j = 0; j < item.h; j++) + frame.push(i + item.x + (j + item.y) * this.w); + return frame; + } + debug() { + return this.grid; + } +} + +export default GridLogic; \ No newline at end of file diff --git a/system/dbupdate_3.4/dbupdate_dashboard.php b/system/dbupdate_3.4/dbupdate_dashboard.php index dcd0e6600..16b8e2d3e 100644 --- a/system/dbupdate_3.4/dbupdate_dashboard.php +++ b/system/dbupdate_3.4/dbupdate_dashboard.php @@ -369,6 +369,7 @@ GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE dashboard.tbl_dashboard_benutzer_over -- Name: SEQUENCE tbl_dashboard_benutzer_override_override_id_seq; Type: ACL; Schema: dashboard; Owner: fhcomplete -- +GRANT ALL ON SEQUENCE dashboard.tbl_dashboard_benutzer_override_override_id_seq TO web; GRANT ALL ON SEQUENCE dashboard.tbl_dashboard_benutzer_override_override_id_seq TO vilesci; @@ -399,6 +400,7 @@ GRANT ALL ON SEQUENCE dashboard.tbl_dashboard_preset_preset_id_seq TO vilesci; -- GRANT SELECT ON TABLE dashboard.tbl_dashboard_widget TO web; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE dashboard.tbl_dashboard_widget TO vilesci; -- @@ -406,6 +408,7 @@ GRANT SELECT ON TABLE dashboard.tbl_dashboard_widget TO web; -- GRANT SELECT ON TABLE dashboard.tbl_widget TO web; +GRANT SELECT,INSERT,UPDATE ON TABLE dashboard.tbl_widget TO vilesci; --