diff --git a/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php b/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php index eb9d02776..31e73b6ba 100644 --- a/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php +++ b/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php @@ -32,11 +32,6 @@ class DashboardAdmin extends FHCAPI_Controller 'createDashboard' => 'dashboard/admin:rw', 'updateDashboard' => 'dashboard/admin:rw', 'deleteDashboard' => 'dashboard/admin:rw', - 'loadWidget' => ['dashboard/benutzer:r', 'dashboard/admin:r'], - 'getAllWidgets' => 'dashboard/admin:r', - 'getWidgetsForDashboard' => ['dashboard/benutzer:rw', 'dashboard/admin:r'], - 'setWidgetAllowed' => 'dashboard/admin:rw', - 'index' => 'dashboard/benutzer:r', 'dummy' => 'dashboard/benutzer:r', 'genWidgetId' => 'dashboard/benutzer:rw', @@ -156,52 +151,6 @@ class DashboardAdmin extends FHCAPI_Controller $this->terminateWithSuccess($result); } - public function getAllWidgets() - { - $dashboard_id = $this->input->get('dashboard_id'); - if(!$dashboard_id) - { - return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Dashboard ID']), self::ERROR_TYPE_GENERAL); - } - //$this->terminateWithError($dashboard_id); - $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id); - - if (isError($result)) - $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); - - $this->terminateWithSuccess(getData($result) ?: []); - } - - public function setWidgetAllowed() - { - $dashboard_id = $this->input->post('dashboard_id'); - $widget_id = $this->input->post('widget_id'); - $action = $this->input->post('action'); - - if ($action == 'add') - { - $result = $this->DashboardWidgetModel->insert([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } - elseif ($action == 'delete') - { - $result = $this->DashboardWidgetModel->delete([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } - else - { - $this->terminateWithError("action value invalid", self::ERROR_TYPE_GENERAL); - } - if (isError($result)) - $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); - - $this->terminateWithSuccess(getData($result) ?: []); - } - //Presets public function funktionen() { diff --git a/application/controllers/api/frontend/v1/dashboard/Widget.php b/application/controllers/api/frontend/v1/dashboard/Widget.php new file mode 100644 index 000000000..cd01d9102 --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/Widget.php @@ -0,0 +1,137 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the back-end + * Provides data to the ajax get calls about the users dashboard + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Widget extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'get' => ['dashboard/benutzer:r', 'dashboard/admin:r'], + 'list' => 'dashboard/admin:r', + 'listAllowed' => ['dashboard/benutzer:rw', 'dashboard/admin:r'], + 'setAllowed' => 'dashboard/admin:rw' + ]); + + // Libraries + $this->load->library('dashboard/DashboardLib'); + + // Models + $this->load->model('dashboard/Widget_model', 'WidgetModel'); + } + + public function get($id) + { + $result = $this->WidgetModel->load($id); + + $widget = $this->getDataOrTerminateWithError($result); + + if (!$widget) + return $this->terminateWithSuccess([ + "widget_id" => 0, + "widget_kurzbz" => "notfound", + "arguments" => [ + "className" => 'alert-danger', + "title" => 'Widget Not Found', + "msg" => 'The widget with the id ' . $id . ' could not be found' + ], + "setup" => [ + "name" => 'Widget Not Found', + "file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'), + "width" => 1, + "height" => 1 + ] + ]); + + $widget = current($widget); + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + + $this->terminateWithSuccess($widget); + } + + public function list($dashboard) + { + $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard); + + $widgets = $this->getDataOrTerminateWithError($result); + + $widgets = array_map(function ($widget) { + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + return $widget; + }, $widgets); + + $this->terminateWithSuccess($widgets); + } + + public function listAllowed($dashboard) + { + $result = $this->WidgetModel->getForDashboard($dashboard); + + $widgets = $this->getDataOrTerminateWithError($result); + + $widgets = array_map(function ($widget) { + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + return $widget; + }, $widgets); + + $this->terminateWithSuccess($widgets); + } + + public function setAllowed() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard', 'Dashboard', 'required'); + $this->form_validation->set_rules('widget', 'Widget', 'required'); + $this->form_validation->set_rules('allowed', 'Allowed', 'required|is_bool'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $data = [ + 'dashboard_id' => $this->input->post('dashboard'), + 'widget_id' => $this->input->post('widget') + ]; + + $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); + + if ($this->input->post('allowed')) + $result = $this->DashoardWidgetModel->insert($data); + else + $result = $this->DashoardWidgetModel->delete($data); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } +} diff --git a/application/controllers/dashboard/Widget.php b/application/controllers/dashboard/Widget.php deleted file mode 100644 index 9966ddc12..000000000 --- a/application/controllers/dashboard/Widget.php +++ /dev/null @@ -1,134 +0,0 @@ - ['dashboard/benutzer:r', 'dashboard/admin:r'], - 'getAll' => 'dashboard/admin:r', - 'getWidgetsForDashboard' => ['dashboard/benutzer:rw', 'dashboard/admin:r'], - 'setAllowed' => 'dashboard/admin:rw' - ) - ); - - $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); - $this->load->model('dashboard/Widget_model', 'WidgetModel'); - $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); - } - - public function index() - { - $widget_id = $this->input->get('id'); - - $widget = $this->WidgetModel->load($widget_id); - - if (isError($widget) || !getData($widget)) - return $this->outputJsonSuccess([ - "widget_id" => 0, - "widget_kurzbz" => "notfound", - "arguments" => [ - "className" => 'alert-danger', - "title" => 'Widget Not Found', - "msg" => 'The widget with the id ' . $widget_id . ' could not be found' - ], - "setup" => [ - "name" => 'Widget Not Found', - "file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'), - "width" => 1, - "height" => 1 - ] - ]); - - $widget = current(getData($widget)); - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - - return $this->outputJsonSuccess($widget); - } - - public function getAll() - { - $dashboard_id = $this->input->get('dashboard_id'); - $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id); - - if (isError($result)) - return $this->outputJsonError(getError($result)); - - $tmpwidgets = getData($result) ?: []; - $widgets = array_map(function($widget) { - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - return $widget; - }, $tmpwidgets); - - $this->outputJsonSuccess($widgets); - } - - public function getWidgetsForDashboard() - { - $db = $this->input->get('db'); - $result = $this->WidgetModel->getForDashboard($db); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - $tmpwidgets = getData($result) ?: []; - $widgets = array_map(function($widget) { - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - return $widget; - }, $tmpwidgets); - - $this->outputJsonSuccess($widgets); - } - - public function setAllowed() - { - $input = $this->getPostJSON(); - - $dashboard_id = $input->dashboard_id; - $widget_id = $input->widget_id; - $action = $input->action; - - if ($action == 'add') { - $result = $this->DashboardWidgetModel->insert([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } elseif ($action == 'delete') { - $result = $this->DashboardWidgetModel->delete([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } else { - http_response_code(404); // TODO(chris): 400? - $this->terminateWithJsonError([ - 'error' => 'action value invalid' - ]); - } - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - return $this->outputJsonSuccess(getData($result)); - } -} diff --git a/public/js/api/factory/dashboard/dashboardAdmin.js b/public/js/api/factory/dashboard/dashboardAdmin.js index 4733d0461..4a6745866 100644 --- a/public/js/api/factory/dashboard/dashboardAdmin.js +++ b/public/js/api/factory/dashboard/dashboardAdmin.js @@ -41,20 +41,6 @@ export default { url: 'api/frontend/v1/dashboard/DashboardAdmin/getAllDashboards' }; }, - getAllWidgets(dashboard_id){ - return { - method: 'get', - url: 'api/frontend/v1/dashboard/DashboardAdmin/getAllWidgets', - params: {dashboard_id} - }; - }, - setWidgetAllowed(params){ - return { - method: 'post', - url: 'api/frontend/v1/dashboard/DashboardAdmin/setWidgetAllowed', - params - }; - }, loadFunktionen(dashboard_kurzbz){ return { method: 'get', diff --git a/public/js/api/factory/dashboard/widget.js b/public/js/api/factory/dashboard/widget.js new file mode 100644 index 000000000..d7d1bbc86 --- /dev/null +++ b/public/js/api/factory/dashboard/widget.js @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2026 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + get(widget) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/get/' + widget + }; + }, + list(dashboard) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/list/' + dashboard + }; + }, + listAllowed(dashboard) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/listAllowed/' + dashboard + }; + }, + setAllowed(dashboard_id, widget_id, allowed) { + return { + method: 'post', + url: '/api/frontend/v1/dashboard/widget/setAllowed', + params: { + dashboard_id, widget_id, allowed + } + }; + } +}; \ No newline at end of file diff --git a/public/js/components/Dashboard/Admin/Widgets.js b/public/js/components/Dashboard/Admin/Widgets.js index 60ad6e1b8..203be9d9c 100644 --- a/public/js/components/Dashboard/Admin/Widgets.js +++ b/public/js/components/Dashboard/Admin/Widgets.js @@ -1,4 +1,4 @@ -import ApiDashboardAdmin from "../../../api/factory/dashboard/dashboardAdmin.js"; +import ApiDashboardWidget from "../../../api/factory/dashboard/Widget.js"; export default { emits: [ @@ -12,25 +12,16 @@ export default { methods: { sendChange(widget_id) { let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed; - const params = { - dashboard_id: this.dashboard_id, - widget_id, - action: allow ? 'add' : 'delete' - }; this.$api - .call(ApiDashboardAdmin.setWidgetAllowed(params)) + .call(ApiDashboardWidget.setAllowed(this.dashboard_id, widget_id, allow)) .catch(this.$fhcAlert.handleSystemError); } }, created() { this.$api - .call(ApiDashboardAdmin.getAllWidgets(this.dashboard_id)) + .call(ApiDashboardWidget.list(this.dashboard_id)) .then(result => { -/* console.log(result.data.map(el => ({ - ...el, - ...{setup:JSON.parse(el.setup),arguments:JSON.parse(el.arguments),allowed:!!el.allowed} - })));*/ this.$emit('assignWidgets', result.data.map(el => ({ ...el, ...{setup:JSON.parse(el.setup),arguments:JSON.parse(el.arguments),allowed:!!el.allowed} diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js index c27f0c405..3ed8aac09 100644 --- a/public/js/components/Dashboard/Dashboard.js +++ b/public/js/components/Dashboard/Dashboard.js @@ -2,6 +2,8 @@ import DashboardSection from "./Section.js"; import DashboardWidgetPicker from "./Widget/Picker.js"; import ObjectUtils from "../../helpers/ObjectUtils.js"; +import ApiDashboardWidget from '../../api/factory/dashboard/widget.js'; + export default { name: 'Dashboard', components: { @@ -25,14 +27,14 @@ export default { data() { return { sections: [], - widgets: null, + widgetsSetup: null, editMode: false, } }, provide() { return { editMode: Vue.computed(()=>this.editMode), - widgetsSetup: Vue.computed(() => this.widgets), + widgetsSetup: Vue.computed(() => this.widgetsSetup), timezone: Vue.computed(() => this.viewData.timezone) } }, @@ -43,17 +45,6 @@ export default { }, methods: { widgetAdd(section_name, widget) { - if (this.widgets === null) { - axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {params:{ - db: this.dashboard - }}).then(res => { - res.data.retval.forEach(widget => { - widget.arguments = JSON.parse(widget.arguments); - widget.setup = JSON.parse(widget.setup); - }); - this.widgets = res.data.retval; - }).catch(err => console.error('ERROR:', err)); - } this.$refs.widgetpicker.getWidget().then(widget_id => { widget.widget = widget_id; widget.id = 'loading_' + String((new Date()).valueOf()); @@ -143,13 +134,13 @@ export default { }, created() { this.$p.loadCategory('dashboard'); - axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', { - params: { - db: this.dashboard - } - }).then(res => { - this.widgets = res.data.retval; - }).catch(err => console.error('ERROR:', err)); + + this.$api + .call(ApiDashboardWidget.listAllowed(this.dashboard)) + .then(res => { + this.widgetsSetup = res.data; + }) + .catch(this.$fhcAlert.handleSystemError); axios.get(this.apiurl + '/Config', {params:{ db: this.dashboard @@ -186,6 +177,6 @@ export default { - + ` } diff --git a/public/js/components/Dashboard/Item.js b/public/js/components/Dashboard/Item.js index 06a03ae3a..1fe67ab45 100644 --- a/public/js/components/Dashboard/Item.js +++ b/public/js/components/Dashboard/Item.js @@ -1,5 +1,5 @@ import BsModal from "../Bootstrap/Modal.js"; -import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; +import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js"; import HeightTransition from "../Tranistion/HeightTransition.js"; export default { @@ -142,8 +142,14 @@ export default { this.isLoading = false; }, }, + setup() { + const { actions } = useCachedWidgetLoader(); + return { + loadWidget: actions.load + }; + }, async created() { - this.widget = await CachedWidgetLoader.loadWidget(this.id); + this.widget = await this.loadWidget(this.id); let component = (await import(this.widget.setup.file)).default; this.$options.components["widget" + this.widget.widget_id] = component; this.component = "widget" + this.widget.widget_id; diff --git a/public/js/components/Dashboard/Section.js b/public/js/components/Dashboard/Section.js index 75cfe8e85..817cc52a3 100644 --- a/public/js/components/Dashboard/Section.js +++ b/public/js/components/Dashboard/Section.js @@ -1,7 +1,7 @@ import BsConfirm from "../Bootstrap/Confirm.js"; import DropGrid from '../Drop/Grid.js' import DashboardItem from "./Item.js"; -import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; +import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js"; import WidgetIcon from "./Widget/WidgetIcon.js" export default { @@ -125,23 +125,23 @@ export default { }, checkResizeLimit(item, w, h) { // NOTE(chris): widgets needs to be loaded for this to work - let widget = CachedWidgetLoader.getWidget(item.widget); + let widget = this.widgetState[item.widget]; if (widget) { - let minmaxW = widget.setup.width; + let minmaxW = { ...widget.setup.width }; if (minmaxW.max) minmaxW.min = minmaxW.min || 1; else - minmaxW = {min:minmaxW,max:minmaxW}; + minmaxW = { min: minmaxW, max: minmaxW }; if (w < minmaxW.min) w = minmaxW.min; if (w > minmaxW.max) w = minmaxW.max; - let minmaxH = widget.setup.height; + let minmaxH = { ...widget.setup.height }; if (minmaxH.max) minmaxH.min = minmaxH.min || 1; else - minmaxH = {min:minmaxH,max:minmaxH}; + minmaxH = { min: minmaxH, max: minmaxH }; if (h < minmaxH.min) h = minmaxH.min; if (h > minmaxH.max) @@ -199,6 +199,13 @@ export default { this.$emit('widgetUpdate', this.name, payload); } }, + setup() { + const { state: widgetState } = useCachedWidgetLoader(); + + return { + widgetState + }; + }, mounted() { let self = this; let cont = self.$refs.container; diff --git a/public/js/composables/Dashboard/CachedWidgetLoader.js b/public/js/composables/Dashboard/CachedWidgetLoader.js index a92e3e557..4bc2d4992 100644 --- a/public/js/composables/Dashboard/CachedWidgetLoader.js +++ b/public/js/composables/Dashboard/CachedWidgetLoader.js @@ -1,29 +1,36 @@ -let __widgets = {}; -let __widgetsStarted = {}; -let __path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard/Widget'; +import ApiWidget from "../../api/factory/dashboard/widget.js"; -export default { - getWidget(id) { - return __widgets[id]; - }, - loadWidget(id) { - if (__widgets[id]) - return Promise.resolve(__widgets[id]); - if (__widgetsStarted[id]) - return __widgetsStarted[id]; - if (!__path) - return Promise.reject('Widget could not be loaded because there is no path yet!'); +const promises = Vue.ref([]); +const stateRef = Vue.ref([]); +const state = Vue.readonly(stateRef); - __widgetsStarted[id] = new Promise((resolve, reject) => { - axios.get(__path, {params:{id}}).then(res => { - __widgets[id] = res.data.retval; - __widgetsStarted[id] = undefined; - resolve(__widgets[id]); - }).catch(error => reject(error.response.data.retval.error)); - }); - return __widgetsStarted[id]; - }, - setPath(path) { - __path = path; +export function useCachedWidgetLoader() { + const $api = Vue.inject('$api'); + const $fhcAlert = Vue.inject('$fhcAlert'); + + function load(id) { + if (state.value[id]) + return Promise.resolve(state.value[id]); + + if (!promises.value[id]) + promises.value[id] = new Promise((resolve, reject) => { + $api + .call(ApiWidget.get(id)) + .then(res => { + stateRef.value[id] = res.data; + promises.value[id] = undefined; + resolve(state.value[id]); + }) + .catch($fhcAlert.handleSystemError); + }); + + return promises.value[id]; } + + return { + state, + actions: { + load + } + }; } \ No newline at end of file