mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
Make add / edit in dashboard interactive, and pass on data
This commit is contained in:
@@ -235,10 +235,13 @@ ibo-dashboard[data-edit-mode="view"] {
|
||||
}
|
||||
|
||||
.ibo-dashboard[data-edit-mode="edit"] .ibo-dashboard--grid:has(ibo-dashboard-grid-slot > ibo-dashlet[data-edit-mode="edit"]) .ibo-dashboard--grid--backdrop {
|
||||
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: $ibo-color-grey-900;
|
||||
height: calc(100% + 24px);
|
||||
// 36px is $ibo-page-container--elements-padding-x, handle variable resolution
|
||||
width: calc(100% + 36px + 36px);
|
||||
margin: -24px -#{36px} 0 -#{36px};
|
||||
background-color: $ibo-color-grey-400;
|
||||
z-index: 2;
|
||||
opacity: 50%;
|
||||
opacity: 60%;
|
||||
}
|
||||
@@ -55,6 +55,18 @@ class IboGrid extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
GetDashletElement(sDashletId) {
|
||||
const aSlots = this.getSlots();
|
||||
|
||||
for (let oSlot of aSlots) {
|
||||
|
||||
if (oSlot.oDashlet && oSlot.oDashlet.sDashletId === sDashletId) {
|
||||
return oSlot.oDashlet;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
AddDashlet(sDashlet, aOptions = {}) {
|
||||
// Get the dashlet as an object
|
||||
const oParser = new DOMParser();
|
||||
@@ -73,6 +85,16 @@ class IboGrid extends HTMLElement {
|
||||
return oDashlet.sDashletId;
|
||||
}
|
||||
|
||||
RefreshDashlet (sDashlet, aOptions = {}) {
|
||||
const oParser = new DOMParser();
|
||||
const oDocument = oParser.parseFromString(sDashlet, 'text/html');
|
||||
const oNewDashlet = oDocument.body.firstChild;
|
||||
debugger;
|
||||
// Can't use oNewDashet.sDashletId as it's not in the DOM yet and connectedCallback hasn't been called yet
|
||||
const oExistingDashlet = this.GetDashletElement(oNewDashlet.getAttribute('data-dashlet-id') );
|
||||
oExistingDashlet.replaceWith(oNewDashlet);
|
||||
}
|
||||
|
||||
CloneDashlet(sDashletId) {
|
||||
const aSlots = this.getSlots();
|
||||
for (let oSlot of aSlots) {
|
||||
|
||||
@@ -84,7 +84,7 @@ class IboDashboard extends HTMLElement {
|
||||
}
|
||||
|
||||
AddNewDashlet(sDashletClass, aDashletOptions = {}) {
|
||||
const sNewDashletUrl = GetAbsoluteUrlAppRoot() + '/pages/UI.php?route=dashboard.new_dashlet&dashlet_class='+encodeURIComponent(sDashletClass);
|
||||
const sNewDashletUrl = GetAbsoluteUrlAppRoot() + '/pages/UI.php?route=dashboard.get_dashlet&dashlet_class='+encodeURIComponent(sDashletClass);
|
||||
fetch(sNewDashletUrl)
|
||||
.then(async data => {
|
||||
|
||||
@@ -92,31 +92,52 @@ class IboDashboard extends HTMLElement {
|
||||
|
||||
// TODO 3.3 Either open the dashlet form right away, or just enter edit mode
|
||||
this.EditDashlet(sDashletId);
|
||||
|
||||
const sGetashletFormUrl = GetAbsoluteUrlAppRoot() + '/pages/UI.php?route=dashboard.get_dashlet_form&dashlet_class='+encodeURIComponent(sDashletClass);
|
||||
fetch(sGetashletFormUrl)
|
||||
.then(async formData => {
|
||||
const sFormData = await formData.text();
|
||||
|
||||
this.HideDashletTogglers();
|
||||
this.SetDashletForm(sFormData);
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
RefreshDashlet(sDashletId) {
|
||||
const oDashletElem = this.oGrid.GetDashletElement(sDashletId);
|
||||
|
||||
let sGetDashletUrl = GetAbsoluteUrlAppRoot() + '/pages/UI.php?route=dashboard.get_dashlet&dashlet_class=' + encodeURIComponent(oDashletElem.sType)
|
||||
+'&dashlet_id=' + encodeURIComponent(oDashletElem.sDashletId);
|
||||
if(oDashletElem.formData.length > 0) {
|
||||
sGetDashletUrl += '&values=' + encodeURIComponent(oDashletElem.formData);
|
||||
}
|
||||
|
||||
return fetch(sGetDashletUrl)
|
||||
.then(async data => {
|
||||
|
||||
const sDashletId = this.oGrid.RefreshDashlet(await data.text());
|
||||
});
|
||||
}
|
||||
|
||||
HideDashletTogglers() {
|
||||
const aTogglers = document.querySelector('.ibo-dashlet-panel--entries');
|
||||
aTogglers.classList.add('ibo-is-hidden');
|
||||
}
|
||||
|
||||
ShowDashletTogglers() {
|
||||
const aTogglers = document.querySelector('.ibo-dashlet-panel--entries');
|
||||
aTogglers.classList.remove('ibo-is-hidden');
|
||||
}
|
||||
|
||||
SetDashletForm(sFormData) {
|
||||
const oFormContainer = document.querySelector('.ibo-dashlet-panel--form-container');
|
||||
oFormContainer.innerHTML = sFormData;
|
||||
oFormContainer.classList.remove('ibo-is-hidden');
|
||||
}
|
||||
|
||||
ClearDashletForm() {
|
||||
const oFormContainer = document.querySelector('.ibo-dashlet-panel--form-container');
|
||||
oFormContainer.innerHTML = '';
|
||||
oFormContainer.classList.add('ibo-is-hidden');
|
||||
}
|
||||
|
||||
EditDashlet(sDashletId) {
|
||||
console.log(this.oGrid);
|
||||
const oDashlet = this.oGrid.GetDashletElement(sDashletId);
|
||||
const me = this;
|
||||
// TODO 3.3: Implement dashlet editing when forms are ready
|
||||
console.log("Edit dashlet: "+sDashletId);
|
||||
|
||||
@@ -130,6 +151,52 @@ class IboDashboard extends HTMLElement {
|
||||
this.querySelector('ibo-dashlet[data-dashlet-id="'+sDashletId+'"]').setAttribute('data-edit-mode', 'edit');
|
||||
|
||||
// Disable dashboard buttons so we need to finish this edition first
|
||||
|
||||
// Fetch dashlet form from server
|
||||
let sGetashletFormUrl = GetAbsoluteUrlAppRoot() + '/pages/UI.php?route=dashboard.get_dashlet_form&dashlet_class='+encodeURIComponent(oDashlet.sType);
|
||||
|
||||
if(oDashlet.formData.length > 0) {
|
||||
sGetashletFormUrl += '&values=' + encodeURIComponent(oDashlet.formData);
|
||||
}
|
||||
|
||||
fetch(sGetashletFormUrl)
|
||||
.then(async formData => {
|
||||
const sFormData = await formData.text();
|
||||
|
||||
this.HideDashletTogglers();
|
||||
this.SetDashletForm(sFormData);
|
||||
// Listen to form submission event to display
|
||||
document.addEventListener('itop:TurboStreamEvent', function (event) {
|
||||
console.log(event);
|
||||
if(event.detail.id === oDashlet.sType + '-turbo-stream-event' && event.detail.valid === "1") {
|
||||
// Notify it all went well
|
||||
CombodoToast.OpenToast('Dashlet created/updated');
|
||||
|
||||
// Clean edit mode
|
||||
me.querySelector('ibo-dashlet[data-dashlet-id="'+sDashletId+'"]').setAttribute('data-edit-mode', 'view');
|
||||
me.ShowDashletTogglers();
|
||||
me.ClearDashletForm();
|
||||
|
||||
// Update local dashlet and refresh it
|
||||
oDashlet.formData = event.detail.view_data;
|
||||
me.RefreshDashlet(oDashlet.sDashletId);
|
||||
|
||||
// TODO 3.3 Remove both event listener by making this code a dedicated function and call removeEventListener on it
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.ibo-dashlet-panel--form-container button[name="dashboard_cancel"]').addEventListener('click', function (event) {
|
||||
// TODO 3.3 Remove both event listener by making this code a dedicated function and call removeEventListener on it
|
||||
|
||||
// Clean edit mode
|
||||
me.querySelector('ibo-dashlet[data-dashlet-id="'+sDashletId+'"]').setAttribute('data-edit-mode', 'view');
|
||||
me.ShowDashletTogglers();
|
||||
me.ClearDashletForm();
|
||||
|
||||
// TODO 3.3 If this is an addition, remove the previewed dashlet
|
||||
// TODO 3.3 If this is an edition, revert the dashlet to its initial state
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
CloneDashlet(sDashletId) {
|
||||
|
||||
@@ -7,9 +7,9 @@ class IboDashlet extends HTMLElement {
|
||||
/** @type {string} */
|
||||
this.sDashletId = this.GetDashletId();
|
||||
/** @type {string} */
|
||||
this.type = this.GetDashletType();
|
||||
this.sType = this.GetDashletType();
|
||||
/** @type {Object} */
|
||||
this.formData = {};
|
||||
this.formData = this.GetFormData();
|
||||
/** @type {Object} unused yet */
|
||||
this.meta = {};
|
||||
|
||||
@@ -38,6 +38,10 @@ class IboDashlet extends HTMLElement {
|
||||
return this.getAttribute("data-dashlet-type") || "";
|
||||
}
|
||||
|
||||
GetFormData() {
|
||||
return this.getAttribute("data-form-view-data") || "";
|
||||
}
|
||||
|
||||
static MakeNew(sDashlet) {
|
||||
const oDashlet = document.createElement('ibo-dashlet');
|
||||
oDashlet.innerHTML = sDashlet;
|
||||
@@ -48,7 +52,7 @@ class IboDashlet extends HTMLElement {
|
||||
Serialize() {
|
||||
const aDashletData = {
|
||||
id: this.sDashletId,
|
||||
type: this.type,
|
||||
type: this.sType,
|
||||
formData: this.formData,
|
||||
};
|
||||
|
||||
|
||||
@@ -15,10 +15,14 @@ class DashboardController extends AbstractController
|
||||
{
|
||||
public const ROUTE_NAMESPACE = 'dashboard';
|
||||
|
||||
public function OperationNewDashlet()
|
||||
public function OperationGetDashlet()
|
||||
{
|
||||
$sDashletClass = utils::ReadParam('dashlet_class', '', false, utils::ENUM_SANITIZATION_FILTER_PHP_CLASS);
|
||||
$sDashletId = utils::ReadParam('dashlet_id', '', false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
// TODO 3.3 Check if raw data is the right call
|
||||
$sValues = utils::ReadParam('values', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$aValues = !empty($sValues) ? json_decode($sValues, true) : [];
|
||||
|
||||
$oPage = new AjaxPage('');
|
||||
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet')) {
|
||||
@@ -26,13 +30,16 @@ class DashboardController extends AbstractController
|
||||
$sDashletId = !empty($sDashletId) ? $sDashletId : uniqid();
|
||||
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId);
|
||||
//$offset = $oPage->start_capture();
|
||||
|
||||
// TODO 3.3 I'd like to update dashlet from frontend's normalized data
|
||||
// $oDashlet->FromNormalizedParams($aValues);
|
||||
|
||||
$oDashletBlock = $oDashlet->DoRender($oPage, true /* bEditMode */, false /* bEnclosingDiv */);
|
||||
//$sHtml = addslashes($oPage->end_capture($offset));
|
||||
|
||||
if ($oDashletBlock instanceof iUIBlock) {
|
||||
// Wrap the dashlet
|
||||
$oDashletWrapper = new DashletWrapper($oDashletBlock, $sDashletClass, $oDashlet->GetID());
|
||||
// TODO 3.3 Re-normalize Dashlet's values instead of using user input
|
||||
$oDashletWrapper = new DashletWrapper($oDashletBlock, $sDashletClass, $oDashlet->GetID(), $aValues);
|
||||
$oPage->AddUiBlock($oDashletWrapper);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +50,10 @@ class DashboardController extends AbstractController
|
||||
public function OperationGetDashletForm()
|
||||
{
|
||||
$sDashletClass = utils::ReadParam('dashlet_class', '', false, utils::ENUM_SANITIZATION_FILTER_PHP_CLASS);
|
||||
// TODO 3.3 Do we want to use a readparam here or SF internal mechanism ?
|
||||
$sValues = utils::ReadParam('values', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$aValues = !empty($sValues) ? json_decode($sValues, true) : [];
|
||||
|
||||
$oPage = new AjaxPage('');
|
||||
|
||||
$oUIBlock = TurboFormUIBlockFactory::MakeForDashletConfiguration($sDashletClass);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<ibo-dashlet class="grid-stack-item-content aa" data-dashlet-type="{{ oUIBlock.GetDashletClass() }}" data-dashlet-id="{{ oUIBlock.GetDashletId() }}" data-form-view-data="{{ oUIBlock.GetFormViewData()|json_encode }}">
|
||||
<ibo-dashlet class="grid-stack-item-content aa"
|
||||
data-dashlet-type="{{ oUIBlock.GetDashletClass() }}"
|
||||
data-dashlet-id="{{ oUIBlock.GetDashletId() }}"
|
||||
data-form-view-data="{{ oUIBlock.GetFormViewData()|json_encode }}">
|
||||
<div class="ibo-dashlet--actions">
|
||||
<button class="ibo-dashlet--action ibo-dashlet--action--edit ibo-button ibo-is-alternative ibo-is-neutral"
|
||||
title="Edit dashlet"
|
||||
|
||||
Reference in New Issue
Block a user