From cb74ff72e21e4489470b6288cd5954c4ce98ac6f Mon Sep 17 00:00:00 2001
From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com>
Date: Thu, 14 Nov 2024 16:02:17 +0100
Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20[Frontend]=20Enh:=20Tag=20manage?=
=?UTF-8?q?ment=20(#6720)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../client/source/class/osparc/NewRelease.js | 20 +-
.../source/class/osparc/NewUITracker.js | 33 ++--
.../source/class/osparc/dashboard/CardBase.js | 2 +-
.../class/osparc/dashboard/Dashboard.js | 3 -
.../class/osparc/dashboard/GridButtonItem.js | 2 +-
.../class/osparc/dashboard/ListButtonItem.js | 2 +-
.../dashboard/ResourceContainerManager.js | 10 +-
.../class/osparc/dashboard/ResourceFilter.js | 13 +-
.../class/osparc/dashboard/SearchBarFilter.js | 17 +-
.../source/class/osparc/data/model/Tag.js | 86 +++++++++
.../source/class/osparc/desktop/MainPage.js | 2 +-
.../class/osparc/desktop/MainPageDesktop.js | 2 +-
.../desktop/preferences/pages/TagsPage.js | 11 +-
.../class/osparc/filter/UserTagsFilter.js | 6 +-
.../source/class/osparc/form/tag/TagItem.js | 181 ++++++++++--------
.../class/osparc/form/tag/TagManager.js | 15 +-
.../class/osparc/form/tag/TagToggleButton.js | 8 +-
.../source/class/osparc/info/StudyUtils.js | 4 +-
.../osparc/notification/NotificationUI.js | 12 +-
.../notification/NotificationsContainer.js | 7 +-
.../source/class/osparc/store/Folders.js | 3 +-
.../source/class/osparc/store/Services.js | 6 +-
.../client/source/class/osparc/store/Tags.js | 132 +++++++++++++
.../source/class/osparc/ui/basic/Tag.js | 16 +-
24 files changed, 427 insertions(+), 166 deletions(-)
create mode 100644 services/static-webserver/client/source/class/osparc/data/model/Tag.js
create mode 100644 services/static-webserver/client/source/class/osparc/store/Tags.js
diff --git a/services/static-webserver/client/source/class/osparc/NewRelease.js b/services/static-webserver/client/source/class/osparc/NewRelease.js
index af6c23f34eb..bac9d1efb25 100644
--- a/services/static-webserver/client/source/class/osparc/NewRelease.js
+++ b/services/static-webserver/client/source/class/osparc/NewRelease.js
@@ -44,13 +44,19 @@ qx.Class.define("osparc.NewRelease", {
/**
* Compare the latest version provided by the backend with the one loaded in the browser (might be an old cached one)
*/
- isMyFrontendOld: async function() {
- const lastUICommit = await osparc.store.AppSummary.getLatestUIFromBE();
- const thisUICommit = osparc.utils.LibVersions.getVcsRefUI();
- if (lastUICommit && thisUICommit) {
- return lastUICommit !== thisUICommit;
- }
- return false;
+ isMyFrontendOld: function() {
+ return new Promise((resolve, reject) => {
+ osparc.store.AppSummary.getLatestUIFromBE()
+ .then(lastUICommit => {
+ const thisUICommit = osparc.utils.LibVersions.getVcsRefUI();
+ if (lastUICommit && thisUICommit) {
+ resolve(lastUICommit !== thisUICommit)
+ } else {
+ reject();
+ }
+ })
+ .catch(() => reject());
+ });
}
},
diff --git a/services/static-webserver/client/source/class/osparc/NewUITracker.js b/services/static-webserver/client/source/class/osparc/NewUITracker.js
index 04a19536128..c85fb3f9390 100644
--- a/services/static-webserver/client/source/class/osparc/NewUITracker.js
+++ b/services/static-webserver/client/source/class/osparc/NewUITracker.js
@@ -27,21 +27,24 @@ qx.Class.define("osparc.NewUITracker", {
__checkInterval: null,
startTracker: function() {
- const checkNewUI = async () => {
- const newReleaseAvailable = await osparc.NewRelease.isMyFrontendOld();
- if (newReleaseAvailable) {
- let msg = "";
- msg += qx.locale.Manager.tr("A new version of the application is now available.");
- msg += "
";
- msg += qx.locale.Manager.tr("Click the Reload button to get the latest features.");
- // permanent message
- const flashMessage = osparc.FlashMessenger.getInstance().logAs(msg, "INFO", 0).set({
- maxWidth: 500
- });
- const reloadButton = osparc.utils.Utils.reloadNoCacheButton();
- flashMessage.addWidget(reloadButton);
- this.stopTracker();
- }
+ const checkNewUI = () => {
+ osparc.NewRelease.isMyFrontendOld()
+ .then(newReleaseAvailable => {
+ if (newReleaseAvailable) {
+ let msg = "";
+ msg += qx.locale.Manager.tr("A new version of the application is now available.");
+ msg += "
";
+ msg += qx.locale.Manager.tr("Click the Reload button to get the latest features.");
+ // permanent message
+ const flashMessage = osparc.FlashMessenger.getInstance().logAs(msg, "INFO", 0).set({
+ maxWidth: 500
+ });
+ const reloadButton = osparc.utils.Utils.reloadNoCacheButton();
+ flashMessage.addWidget(reloadButton);
+ this.stopTracker();
+ }
+ })
+ .catch(() => setTimeout(() => checkNewUI(), 5*1000));
};
checkNewUI();
this.__checkInterval = setInterval(checkNewUI, this.self().CHECK_INTERVAL);
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
index 8d59dee3728..1b7a8fe6e82 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js
@@ -926,7 +926,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
},
_filterTags: function(tags) {
- const checks = this.getTags().map(tag => tag.id);
+ const checks = this.getTags().map(tag => tag.getTagId());
return this.self().filterTags(checks, tags);
},
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js
index cc714440242..4a1420ade43 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js
@@ -181,9 +181,6 @@ qx.Class.define("osparc.dashboard.Dashboard", {
const store = osparc.store.Store.getInstance();
preResourcePromises.push(store.getAllGroupsAndMembers());
preResourcePromises.push(osparc.store.Services.getServicesLatest(false));
- if (permissions.canDo("study.tag")) {
- preResourcePromises.push(osparc.data.Resources.get("tags"));
- }
Promise.all(preResourcePromises)
.then(() => {
[
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
index 148a6b114bb..828a0c74ba7 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js
@@ -262,7 +262,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", {
tagsContainer.setVisibility(tags.length ? "visible" : "excluded");
tagsContainer.removeAll();
tags.forEach(tag => {
- const tagUI = new osparc.ui.basic.Tag(tag.name, tag.color, "searchBarFilter");
+ const tagUI = new osparc.ui.basic.Tag(tag, "searchBarFilter");
tagUI.set({
font: "text-12",
toolTipText: this.tr("Click to filter by this Tag")
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
index e89e03a0943..71f59b970df 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js
@@ -237,7 +237,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", {
const tagsContainer = this.getChildControl("tags");
tagsContainer.removeAll();
tags.forEach(tag => {
- const tagUI = new osparc.ui.basic.Tag(tag.name, tag.color, "searchBarFilter");
+ const tagUI = new osparc.ui.basic.Tag(tag, "searchBarFilter");
tagUI.set({
alignY: "middle",
font: "text-12",
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
index b28b5d89a04..fa99ba050dd 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js
@@ -208,7 +208,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
},
__createCard: function(resourceData) {
- const tags = resourceData.tags ? osparc.store.Store.getInstance().getTags().filter(tag => resourceData.tags.includes(tag.id)) : [];
+ const tags = resourceData.tags ? osparc.store.Tags.getInstance().getTags().filter(tag => resourceData.tags.includes(tag.getTagId())) : [];
const card = this.getMode() === "grid" ? new osparc.dashboard.GridButtonItem() : new osparc.dashboard.ListButtonItem();
card.set({
appearance: resourceData.type ? `pb-${resourceData.type}` : `pb-${resourceData.resourceType}`,
@@ -434,7 +434,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
},
__groupByTags: function(cards, resourceData) {
- const tags = resourceData.tags ? osparc.store.Store.getInstance().getTags().filter(tag => resourceData.tags.includes(tag.id)) : [];
+ const tags = resourceData.tags ? osparc.store.Tags.getInstance().getTags().filter(tag => resourceData.tags.includes(tag.getTagId())) : [];
if (tags.length === 0) {
let noGroupContainer = this.__getGroupContainer("no-group");
const card = this.__createCard(resourceData);
@@ -443,9 +443,11 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
cards.push(card);
} else {
tags.forEach(tag => {
- let groupContainer = this.__getGroupContainer(tag.id);
+ let groupContainer = this.__getGroupContainer(tag.getTagId());
if (groupContainer === null) {
- groupContainer = this.__createGroupContainer(tag.id, tag.name, tag.color);
+ groupContainer = this.__createGroupContainer(tag.getTagId(), tag.getName(), tag.getColor());
+ tag.bind("name", groupContainer, "headerLabel");
+ tag.bind("color", groupContainer, "headerColor");
groupContainer.setHeaderIcon("@FontAwesome5Solid/tag/24");
this.__groupedContainers.add(groupContainer);
this.__groupedContainers.getChildren().sort((a, b) => a.getHeaderLabel().localeCompare(b.getHeaderLabel()));
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js
index 142cdab7d3f..0c452e3e33a 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js
@@ -158,16 +158,15 @@ qx.Class.define("osparc.dashboard.ResourceFilter", {
const maxTags = 5;
this.__tagButtons = [];
layout.removeAll();
- osparc.store.Store.getInstance().getTags().forEach((tag, idx) => {
- const button = new qx.ui.form.ToggleButton(tag.name, "@FontAwesome5Solid/tag/18");
+ osparc.store.Tags.getInstance().getTags().forEach((tag, idx) => {
+ const button = new qx.ui.form.ToggleButton(null, "@FontAwesome5Solid/tag/18");
+ button.id = tag.getTagId();
+ tag.bind("name", button, "label");
+ tag.bind("color", button.getChildControl("icon"), "textColor");
osparc.utils.Utils.setIdToWidget(button, this.__resourceType + "-tagFilterItem");
- button.id = tag.id;
button.set({
appearance: "filter-toggle-button",
- value: selectedTagIds.includes(tag.id)
- });
- button.getChildControl("icon").set({
- textColor: tag.color
+ value: selectedTagIds.includes(tag.getTagId())
});
layout.add(button);
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js
index b836a93ef44..5b376a6b404 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js
@@ -208,14 +208,14 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", {
},
__addTags: function(menuButton) {
- const tags = osparc.store.Store.getInstance().getTags();
+ const tags = osparc.store.Tags.getInstance().getTags();
menuButton.setVisibility(tags.length ? "visible" : "excluded");
if (tags.length) {
const tagsMenu = new qx.ui.menu.Menu();
osparc.utils.Utils.setIdToWidget(tagsMenu, "searchBarFilter-tags-menu");
tags.forEach(tag => {
- const tagButton = new qx.ui.menu.Button(tag.name, "@FontAwesome5Solid/tag/12");
- tagButton.getChildControl("icon").setTextColor(tag.color);
+ const tagButton = new qx.ui.menu.Button(tag.getName(), "@FontAwesome5Solid/tag/12");
+ tagButton.getChildControl("icon").setTextColor(tag.getColor());
tagsMenu.add(tagButton);
tagButton.addListener("execute", () => this.addTagActiveFilter(tag), this);
});
@@ -271,16 +271,17 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", {
},
addTagActiveFilter: function(tag) {
- this.__addChip("tag", tag.id, tag.name);
+ this.__addChip("tag", tag.getTagId(), tag.getName());
},
setTagsActiveFilter: function(tagIds) {
- const tags = osparc.store.Store.getInstance().getTags();
+ const tags = osparc.store.Tags.getInstance().getTags();
tags.forEach(tag => {
- if (tagIds.includes(tag.id)) {
- this.__addChip("tag", tag.id, tag.name);
+ const tagId = tag.getTagId();
+ if (tagIds.includes(tagId)) {
+ this.__addChip("tag", tagId, tag.getName());
} else {
- this.__removeChip("tag", tag.id, tag.name);
+ this.__removeChip("tag", tagId, tag.getName());
}
});
},
diff --git a/services/static-webserver/client/source/class/osparc/data/model/Tag.js b/services/static-webserver/client/source/class/osparc/data/model/Tag.js
new file mode 100644
index 00000000000..fc7e00a5fcc
--- /dev/null
+++ b/services/static-webserver/client/source/class/osparc/data/model/Tag.js
@@ -0,0 +1,86 @@
+/* ************************************************************************
+
+ osparc - the simcore frontend
+
+ https://osparc.io
+
+ Copyright:
+ 2024 IT'IS Foundation, https://itis.swiss
+
+ License:
+ MIT: https://opensource.org/licenses/MIT
+
+ Authors:
+ * Odei Maiz (odeimaiz)
+
+************************************************************************ */
+
+/**
+ * Class that stores Tag data.
+ */
+
+qx.Class.define("osparc.data.model.Tag", {
+ extend: qx.core.Object,
+
+ /**
+ * @param tagData {Object} Object containing the serialized Tag Data
+ */
+ construct: function(tagData) {
+ this.base(arguments);
+
+ this.set({
+ tagId: tagData.id,
+ name: tagData.name,
+ description: tagData.description,
+ color: tagData.color,
+ accessRights: tagData.accessRights,
+ });
+ },
+
+ properties: {
+ tagId: {
+ check: "Number",
+ nullable: true,
+ init: null,
+ event: "changeTagId"
+ },
+
+ name: {
+ check: "String",
+ nullable: false,
+ init: null,
+ event: "changeName"
+ },
+
+ description: {
+ check: "String",
+ nullable: true,
+ init: null,
+ event: "changeDescription"
+ },
+
+ color: {
+ check: "Color",
+ event: "changeColor",
+ init: "#303030"
+ },
+
+ accessRights: {
+ check: "Object",
+ nullable: false,
+ init: null,
+ event: "changeAccessRights"
+ },
+ },
+
+ members: {
+ serialize: function() {
+ const jsonObject = {};
+ const propertyKeys = this.self().getProperties();
+ propertyKeys.forEach(key => {
+ jsonObject[key] = this.get(key);
+ });
+ return jsonObject;
+ }
+ }
+});
diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js
index d2b72acfdcc..0ccb9bbe8b9 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js
@@ -66,7 +66,7 @@ qx.Class.define("osparc.desktop.MainPage", {
preloadPromises.push(store.reloadWallets());
}
preloadPromises.push(store.getAllClassifiers(true));
- preloadPromises.push(store.getTags());
+ preloadPromises.push(osparc.store.Tags.getInstance().fetchTags());
Promise.all(preloadPromises)
.then(() => {
const mainStack = this.__createMainStack();
diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPageDesktop.js b/services/static-webserver/client/source/class/osparc/desktop/MainPageDesktop.js
index 93f5f50c74d..40c99616a40 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/MainPageDesktop.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/MainPageDesktop.js
@@ -61,7 +61,7 @@ qx.Class.define("osparc.desktop.MainPageDesktop", {
preloadPromises.push(store.reloadWallets());
}
preloadPromises.push(store.getAllClassifiers(true));
- preloadPromises.push(store.getTags());
+ preloadPromises.push(osparc.store.Tags.getInstance().fetchTags());
Promise.all(preloadPromises)
.then(() => {
const desktopCenter = new osparc.desktop.credits.DesktopCenter();
diff --git a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TagsPage.js b/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TagsPage.js
index 7265c65cebd..add2f2f3040 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TagsPage.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TagsPage.js
@@ -48,13 +48,10 @@ qx.Class.define("osparc.desktop.preferences.pages.TagsPage", {
icon: "@FontAwesome5Solid/plus/14"
});
osparc.utils.Utils.setIdToWidget(this.__addTagButton, "addTagBtn");
- osparc.data.Resources.get("tags")
- .then(tags => {
- this.__tagItems = tags.map(tag => new osparc.form.tag.TagItem().set({...tag}));
- this.__renderLayout();
- this.__attachEventHandlers();
- })
- .catch(err => console.error(err));
+ const tags = osparc.store.Tags.getInstance().getTags();
+ this.__tagItems = tags.map(tag => new osparc.form.tag.TagItem().set({tag}));
+ this.__renderLayout();
+ this.__attachEventHandlers();
},
__renderLayout: function() {
diff --git a/services/static-webserver/client/source/class/osparc/filter/UserTagsFilter.js b/services/static-webserver/client/source/class/osparc/filter/UserTagsFilter.js
index c0a74265e01..caf5914e5d3 100644
--- a/services/static-webserver/client/source/class/osparc/filter/UserTagsFilter.js
+++ b/services/static-webserver/client/source/class/osparc/filter/UserTagsFilter.js
@@ -18,11 +18,11 @@ qx.Class.define("osparc.filter.UserTagsFilter", {
},
members: {
__buildMenu: function() {
- osparc.store.Store.getInstance().getTags()
+ osparc.store.Tags.getInstance().getTags()
.forEach(tag => {
- const menuButton = this._addOption(tag.name);
+ const menuButton = this._addOption(tag.getName());
menuButton.setIcon("@FontAwesome5Solid/square/12");
- menuButton.getChildControl("icon").setTextColor(tag.color);
+ menuButton.getChildControl("icon").setTextColor(tag.getColor());
});
},
__attachEventListeners: function(filterId, filterGroupId) {
diff --git a/services/static-webserver/client/source/class/osparc/form/tag/TagItem.js b/services/static-webserver/client/source/class/osparc/form/tag/TagItem.js
index 7e79bb54bf3..77282a5db7f 100644
--- a/services/static-webserver/client/source/class/osparc/form/tag/TagItem.js
+++ b/services/static-webserver/client/source/class/osparc/form/tag/TagItem.js
@@ -26,37 +26,51 @@ qx.Class.define("osparc.form.tag.TagItem", {
},
properties: {
+ tag: {
+ check: "osparc.data.model.Tag",
+ nullable: false,
+ init: null,
+ event: "changeTag",
+ apply: "__applyTag",
+ },
+
id: {
check: "Integer"
},
+
name: {
check: "String",
event: "changeName",
init: ""
},
+
description: {
check: "String",
nullable: true,
event: "changeDescription",
init: ""
},
+
color: {
check: "Color",
event: "changeColor",
init: "#303030"
},
+
accessRights: {
check: "Object",
nullable: false,
+ event: "changeAccessRights",
apply: "__renderLayout",
- event: "changeAccessRights"
},
+
mode: {
check: "String",
init: "display",
nullable: false,
apply: "_applyMode"
},
+
appearance: {
init: "tagitem",
refine: true
@@ -78,57 +92,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
__colorButton: null,
__loadingIcon: null,
__validationManager: null,
- /**
- * Renders this tag item from scratch.
- */
- __renderLayout: function() {
- this._removeAll();
- if (this.getMode() === this.self().modes.EDIT) {
- this.__renderEditMode();
- } else if (this.getMode() === this.self().modes.DISPLAY) {
- this.__renderDisplayMode();
- }
- },
- __renderEditMode: function() {
- const nameContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({
- width: 90
- });
- nameContainer.add(new qx.ui.basic.Label(this.tr("Name")).set({
- buddy: this.getChildControl("nameinput")
- }));
- nameContainer.add(this.getChildControl("nameinput").set({
- value: this.getName()
- }));
- this._add(nameContainer);
- const descInputContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox());
- descInputContainer.add(new qx.ui.basic.Label(this.tr("Description")).set({
- buddy: this.getChildControl("descriptioninput")
- }));
- descInputContainer.add(this.getChildControl("descriptioninput").set({
- value: this.getDescription()
- }));
- this._add(descInputContainer, {
- flex: 1
- });
- this._add(this.__colorPicker());
- this._add(this.__tagItemEditButtons());
- },
- __renderDisplayMode: function() {
- const tagContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox()).set({
- width: 100
- });
- tagContainer.add(this.getChildControl("tag"));
- this._add(tagContainer);
- const descriptionContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox());
- descriptionContainer.add(this.getChildControl("description"), {
- width: "100%"
- });
- this._add(descriptionContainer, {
- flex: 1
- });
- this._add(this.__tagItemButtons());
- this.resetBackgroundColor();
- },
+
_createChildControlImpl: function(id) {
let control;
switch (id) {
@@ -151,7 +115,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
}
control = this.__description;
break;
- case "nameinput":
+ case "name-input":
// Tag name input in edit mode
if (this.__nameInput === null) {
this.__nameInput = new qx.ui.form.TextField().set({
@@ -162,7 +126,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
}
control = this.__nameInput;
break;
- case "descriptioninput":
+ case "description-input":
// Tag description input in edit mode
if (this.__descriptionInput === null) {
this.__descriptionInput = new qx.ui.form.TextArea().set({
@@ -172,7 +136,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
}
control = this.__descriptionInput;
break;
- case "colorinput":
+ case "color-input":
// Color input in edit mode
if (this.__colorInput === null) {
this.__colorInput = new qx.ui.form.TextField().set({
@@ -180,20 +144,20 @@ qx.Class.define("osparc.form.tag.TagItem", {
width: 60,
required: true
});
- this.__colorInput.bind("value", this.getChildControl("colorbutton"), "backgroundColor");
- this.__colorInput.bind("value", this.getChildControl("colorbutton"), "textColor", {
+ this.__colorInput.bind("value", this.getChildControl("color-button"), "backgroundColor");
+ this.__colorInput.bind("value", this.getChildControl("color-button"), "textColor", {
converter: value => osparc.utils.Utils.getContrastedBinaryColor(value)
});
this.__validationManager.add(this.__colorInput, osparc.utils.Validators.hexColor);
}
control = this.__colorInput;
break;
- case "colorbutton":
+ case "color-button":
// Random color generator button in edit mode
if (this.__colorButton === null) {
this.__colorButton = new qx.ui.form.Button(null, "@FontAwesome5Solid/sync-alt/12");
this.__colorButton.addListener("execute", () => {
- this.getChildControl("colorinput").setValue(osparc.utils.Utils.getRandomColor());
+ this.getChildControl("color-input").setValue(osparc.utils.Utils.getRandomColor());
}, this);
}
control = this.__colorButton;
@@ -201,6 +165,69 @@ qx.Class.define("osparc.form.tag.TagItem", {
}
return control || this.base(arguments, id);
},
+
+ __applyTag: function(tag) {
+ tag.bind("tagId", this, "id");
+ tag.bind("name", this, "name");
+ tag.bind("description", this, "description");
+ tag.bind("color", this, "color");
+ tag.bind("accessRights", this, "accessRights");
+ },
+
+ /**
+ * Renders this tag item from scratch.
+ */
+ __renderLayout: function() {
+ this._removeAll();
+ if (this.getMode() === this.self().modes.EDIT) {
+ this.__renderEditMode();
+ } else if (this.getMode() === this.self().modes.DISPLAY) {
+ this.__renderDisplayMode();
+ }
+ },
+
+ __renderEditMode: function() {
+ const nameContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({
+ width: 90
+ });
+ nameContainer.add(new qx.ui.basic.Label(this.tr("Name")).set({
+ buddy: this.getChildControl("name-input")
+ }));
+ nameContainer.add(this.getChildControl("name-input").set({
+ value: this.getName()
+ }));
+ this._add(nameContainer);
+ const descInputContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox());
+ descInputContainer.add(new qx.ui.basic.Label(this.tr("Description")).set({
+ buddy: this.getChildControl("description-input")
+ }));
+ descInputContainer.add(this.getChildControl("description-input").set({
+ value: this.getDescription()
+ }));
+ this._add(descInputContainer, {
+ flex: 1
+ });
+ this._add(this.__colorPicker());
+ this._add(this.__tagItemEditButtons());
+ },
+
+ __renderDisplayMode: function() {
+ const tagContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox()).set({
+ width: 100
+ });
+ tagContainer.add(this.getChildControl("tag"));
+ this._add(tagContainer);
+ const descriptionContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox());
+ descriptionContainer.add(this.getChildControl("description"), {
+ width: "100%"
+ });
+ this._add(descriptionContainer, {
+ flex: 1
+ });
+ this._add(this.__tagItemButtons());
+ this.resetBackgroundColor();
+ },
+
/**
* Generates and returns the buttons for deleting and editing an existing label (display mode)
*/
@@ -224,12 +251,7 @@ qx.Class.define("osparc.form.tag.TagItem", {
editButton.addListener("execute", () => this.setMode(this.self().modes.EDIT), this);
deleteButton.addListener("execute", () => {
deleteButton.setFetching(true);
- const params = {
- url: {
- tagId: this.getId()
- }
- };
- osparc.data.Resources.fetch("tags", "delete", params)
+ osparc.store.Tags.getInstance().deleteTag(this.getId())
.then(() => this.fireEvent("deleteTag"))
.catch(console.error)
.finally(() => deleteButton.setFetching(false));
@@ -256,21 +278,15 @@ qx.Class.define("osparc.form.tag.TagItem", {
saveButton.addListener("execute", () => {
if (this.__validationManager.validate()) {
const data = this.__serializeData();
- const params = {
- data
- };
saveButton.setFetching(true);
let fetch;
if (this.isPropertyInitialized("id")) {
- params.url = {
- tagId: this.getId()
- };
- fetch = osparc.data.Resources.fetch("tags", "put", params);
+ fetch = osparc.store.Tags.getInstance().putTag(this.getId(), data);
} else {
- fetch = osparc.data.Resources.fetch("tags", "post", params);
+ fetch = osparc.store.Tags.getInstance().postTag(data);
}
fetch
- .then(tag => this.set(tag))
+ .then(tag => this.setTag(tag))
.catch(console.error)
.finally(() => {
this.fireEvent("tagSaved");
@@ -295,24 +311,27 @@ qx.Class.define("osparc.form.tag.TagItem", {
__colorPicker: function() {
const container = new qx.ui.container.Composite(new qx.ui.layout.VBox());
container.add(new qx.ui.basic.Label(this.tr("Color")).set({
- buddy: this.getChildControl("colorinput")
+ buddy: this.getChildControl("color-input")
}));
const innerContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox());
- const refreshButton = this.getChildControl("colorbutton");
- const colorInput = this.getChildControl("colorinput");
+ const refreshButton = this.getChildControl("color-button");
+ const colorInput = this.getChildControl("color-input");
innerContainer.add(refreshButton);
innerContainer.add(colorInput);
container.add(innerContainer);
return container;
},
/**
- * Creates an object containing the udpated tag info
+ * Creates an object containing the updated tag info
*/
__serializeData: function() {
+ const name = this.getChildControl("name-input").getValue();
+ const description = this.getChildControl("description-input").getValue();
+ const color = this.getChildControl("color-input").getValue();
return {
- name: this.getChildControl("nameinput").getValue().trim(),
- description: this.getChildControl("descriptioninput").getValue().trim(),
- color: this.getChildControl("colorinput").getValue()
+ name: name.trim(),
+ description: description ? description.trim() : "",
+ color: color
};
},
_applyMode: function() {
diff --git a/services/static-webserver/client/source/class/osparc/form/tag/TagManager.js b/services/static-webserver/client/source/class/osparc/form/tag/TagManager.js
index ae3ef918adb..6f704c1f222 100644
--- a/services/static-webserver/client/source/class/osparc/form/tag/TagManager.js
+++ b/services/static-webserver/client/source/class/osparc/form/tag/TagManager.js
@@ -88,8 +88,8 @@ qx.Class.define("osparc.form.tag.TagManager", {
newItem.addListener("tagSaved", () => this.__repopulateTags(), this);
newItem.addListener("cancelNewTag", e => tagsContainer.remove(e.getTarget()), this);
newItem.addListener("deleteTag", e => tagsContainer.remove(e.getTarget()), this);
- this.__repopulateTags();
tagsContainer.add(newItem);
+ this.__repopulateTags();
});
this._add(addTagButton);
@@ -119,25 +119,26 @@ qx.Class.define("osparc.form.tag.TagManager", {
__repopulateTags: function() {
this.__tagsContainer.removeAll();
- const tags = osparc.store.Store.getInstance().getTags();
+ const tags = osparc.store.Tags.getInstance().getTags();
tags.forEach(tag => this.__tagsContainer.add(this.__tagButton(tag)));
},
__tagButton: function(tag) {
- const tagButton = new osparc.form.tag.TagToggleButton(tag, this.__selectedTags.includes(tag.id));
+ const tagId = tag.getTagId();
+ const tagButton = new osparc.form.tag.TagToggleButton(tag, this.__selectedTags.includes(tagId));
tagButton.addListener("changeValue", evt => {
const selected = evt.getData();
if (this.isLiveUpdate()) {
tagButton.setFetching(true);
if (selected) {
- this.__saveAddTag(tag.id, tagButton);
+ this.__saveAddTag(tagId, tagButton);
} else {
- this.__saveRemoveTag(tag.id, tagButton);
+ this.__saveRemoveTag(tagId, tagButton);
}
} else if (selected) {
- this.__selectedTags.push(tag.id);
+ this.__selectedTags.push(tagId);
} else {
- this.__selectedTags.remove(tag.id);
+ this.__selectedTags.remove(tagId);
}
}, this);
tagButton.subscribeToFilterGroup("studyBrowserTagManager");
diff --git a/services/static-webserver/client/source/class/osparc/form/tag/TagToggleButton.js b/services/static-webserver/client/source/class/osparc/form/tag/TagToggleButton.js
index 3075d738cf3..35feee0c3bc 100644
--- a/services/static-webserver/client/source/class/osparc/form/tag/TagToggleButton.js
+++ b/services/static-webserver/client/source/class/osparc/form/tag/TagToggleButton.js
@@ -23,11 +23,11 @@ qx.Class.define("osparc.form.tag.TagToggleButton", {
appearance: "tagbutton"
});
this.setIcon("@FontAwesome5Solid/square/14");
- this.getChildControl("icon").setTextColor(tag.color);
- if (tag.description) {
- this.setLabel(tag.name + " : " + tag.description);
+ this.getChildControl("icon").setTextColor(tag.getColor());
+ if (tag.getDescription()) {
+ this.setLabel(tag.getName() + " : " + tag.getDescription());
} else {
- this.setLabel(tag.name);
+ this.setLabel(tag.getName());
}
this.getChildControl("check");
diff --git a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js
index 95ea7f20b7f..f1d2c3449e5 100644
--- a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js
+++ b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js
@@ -211,12 +211,12 @@ qx.Class.define("osparc.info.StudyUtils", {
tagsContainer.removeAll();
const noTagsLabel = new qx.ui.basic.Label(qx.locale.Manager.tr("Add tags"));
tagsContainer.add(noTagsLabel);
- osparc.store.Store.getInstance().getTags().filter(tag => model.getTags().includes(tag.id))
+ osparc.store.Tags.getInstance().getTags().filter(tag => model.getTags().includes(tag.getTagId()))
.forEach(selectedTag => {
if (tagsContainer.indexOf(noTagsLabel) > -1) {
tagsContainer.remove(noTagsLabel);
}
- tagsContainer.add(new osparc.ui.basic.Tag(selectedTag.name, selectedTag.color));
+ tagsContainer.add(new osparc.ui.basic.Tag(selectedTag));
});
};
study.addListener("changeTags", () => addTags(study), this);
diff --git a/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js b/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js
index da49db7f0a4..67194c84418 100644
--- a/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js
+++ b/services/static-webserver/client/source/class/osparc/notification/NotificationUI.js
@@ -22,6 +22,7 @@ qx.Class.define("osparc.notification.NotificationUI", {
this.base(arguments);
this.set({
+ margin: 4,
maxWidth: this.self().MAX_WIDTH,
padding: this.self().PADDING,
cursor: "pointer"
@@ -216,9 +217,14 @@ qx.Class.define("osparc.notification.NotificationUI", {
}
});
- notification.bind("read", this, "backgroundColor", {
- converter: read => read ? "background-main-3" : "background-main-4"
- });
+ const highlight = mouseOn => {
+ this.set({
+ backgroundColor: mouseOn ? "strong-main" : "transparent"
+ })
+ };
+ this.addListener("mouseover", () => highlight(true));
+ this.addListener("mouseout", () => highlight(false));
+ highlight(false);
},
__notificationTapped: function() {
diff --git a/services/static-webserver/client/source/class/osparc/notification/NotificationsContainer.js b/services/static-webserver/client/source/class/osparc/notification/NotificationsContainer.js
index 34757474f64..c59a8a94a4c 100644
--- a/services/static-webserver/client/source/class/osparc/notification/NotificationsContainer.js
+++ b/services/static-webserver/client/source/class/osparc/notification/NotificationsContainer.js
@@ -27,9 +27,14 @@ qx.Class.define("osparc.notification.NotificationsContainer", {
zIndex: osparc.utils.Utils.FLOATING_Z_INDEX,
maxWidth: osparc.notification.NotificationUI.MAX_WIDTH,
maxHeight: 250,
- backgroundColor: "background-main-3",
+ backgroundColor: "background-main",
decorator: "rounded",
});
+ let color = qx.theme.manager.Color.getInstance().resolve("text");
+ color = qx.util.ColorUtil.stringToRgb(color);
+ color.push(0.3); // add transparency
+ color = qx.util.ColorUtil.rgbToRgbString(color);
+ osparc.utils.Utils.addBorder(this, 1, color);
osparc.utils.Utils.setIdToWidget(this, "notificationsContainer");
const root = qx.core.Init.getApplication().getRoot();
diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js
index 7deb66618bb..d6e83d8fb23 100644
--- a/services/static-webserver/client/source/class/osparc/store/Folders.js
+++ b/services/static-webserver/client/source/class/osparc/store/Folders.js
@@ -172,6 +172,7 @@ qx.Class.define("osparc.store.Folders", {
__addToCache: function(folderData) {
let folder = this.foldersCached.find(f => f.getFolderId() === folderData["folderId"] && f.getWorkspaceId() === folderData["workspaceId"]);
if (folder) {
+ const props = Object.keys(qx.util.PropertyUtil.getProperties(osparc.data.model.Folder));
// put
Object.keys(folderData).forEach(key => {
if (key === "createdAt") {
@@ -180,7 +181,7 @@ qx.Class.define("osparc.store.Folders", {
folder.set("lastModified", new Date(folderData["modifiedAt"]));
} else if (key === "trashedAt") {
folder.set("trashedAt", new Date(folderData["trashedAt"]));
- } else {
+ } else if (props.includes(key)) {
folder.set(key, folderData[key]);
}
});
diff --git a/services/static-webserver/client/source/class/osparc/store/Services.js b/services/static-webserver/client/source/class/osparc/store/Services.js
index f6851b3aa43..c2abeed32ec 100644
--- a/services/static-webserver/client/source/class/osparc/store/Services.js
+++ b/services/static-webserver/client/source/class/osparc/store/Services.js
@@ -44,7 +44,11 @@ qx.Class.define("osparc.store.Services", {
resolve(servicesObj);
})
- .catch(err => console.error("getServices failed", err));
+ .catch(err => {
+ const msg = err.message || qx.locale.Manager.tr("Unable to fetch Services");
+ osparc.FlashMessenger.getInstance().logAs(msg, "ERROR");
+ console.error(err);
+ });
});
},
diff --git a/services/static-webserver/client/source/class/osparc/store/Tags.js b/services/static-webserver/client/source/class/osparc/store/Tags.js
new file mode 100644
index 00000000000..4ffd9f5cd4f
--- /dev/null
+++ b/services/static-webserver/client/source/class/osparc/store/Tags.js
@@ -0,0 +1,132 @@
+/* ************************************************************************
+
+ osparc - the simcore frontend
+
+ https://osparc.io
+
+ Copyright:
+ 2024 IT'IS Foundation, https://itis.swiss
+
+ License:
+ MIT: https://opensource.org/licenses/MIT
+
+ Authors:
+ * Odei Maiz (odeimaiz)
+
+************************************************************************ */
+
+qx.Class.define("osparc.store.Tags", {
+ extend: qx.core.Object,
+ type: "singleton",
+
+ construct: function() {
+ this.base(arguments);
+
+ this.tagsCached = [];
+ },
+
+ events: {
+ "tagAdded": "qx.event.type.Data",
+ "tagRemoved": "qx.event.type.Data",
+ },
+
+ members: {
+ tagsCached: null,
+
+ fetchTags: function() {
+ if (osparc.auth.Data.getInstance().isGuest()) {
+ return new Promise(resolve => {
+ resolve([]);
+ });
+ }
+
+ return osparc.data.Resources.get("tags")
+ .then(tagsData => {
+ const tags = [];
+ tagsData.forEach(tagData => {
+ const tag = this.__addToCache(tagData);
+ tags.push(tag);
+ });
+ return tags;
+ });
+ },
+
+ getTags: function() {
+ return this.tagsCached;
+ },
+
+ postTag: function(newTagData) {
+ const params = {
+ data: newTagData
+ };
+ return osparc.data.Resources.getInstance().fetch("tags", "post", params)
+ .then(tagData => {
+ const tag = this.__addToCache(tagData);
+ this.fireDataEvent("tagAdded", tag);
+ return tag;
+ });
+ },
+
+ deleteTag: function(tagId) {
+ const params = {
+ url: {
+ tagId
+ }
+ };
+ return osparc.data.Resources.getInstance().fetch("tags", "delete", params)
+ .then(() => {
+ const tag = this.getTag(tagId);
+ if (tag) {
+ this.__deleteFromCache(tagId);
+ this.fireDataEvent("tagRemoved", tag);
+ }
+ })
+ .catch(console.error);
+ },
+
+ putTag: function(tagId, updateData) {
+ const params = {
+ url: {
+ tagId
+ },
+ data: updateData
+ };
+ return osparc.data.Resources.getInstance().fetch("tags", "put", params)
+ .then(tagData => {
+ return this.__addToCache(tagData);
+ })
+ .catch(console.error);
+ },
+
+ getTag: function(tagId = null) {
+ return this.tagsCached.find(f => f.getTagId() === tagId);
+ },
+
+ __addToCache: function(tagData) {
+ let tag = this.tagsCached.find(f => f.getTagId() === tagData["id"]);
+ if (tag) {
+ const props = Object.keys(qx.util.PropertyUtil.getProperties(osparc.data.model.Tag));
+ // put
+ Object.keys(tagData).forEach(key => {
+ if (props.includes(key)) {
+ tag.set(key, tagData[key]);
+ }
+ });
+ } else {
+ // get and post
+ tag = new osparc.data.model.Tag(tagData);
+ this.tagsCached.unshift(tag);
+ }
+ return tag;
+ },
+
+ __deleteFromCache: function(tagId) {
+ const idx = this.tagsCached.findIndex(f => f.getTagId() === tagId);
+ if (idx > -1) {
+ this.tagsCached.splice(idx, 1);
+ return true;
+ }
+ return false;
+ }
+ }
+});
diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/Tag.js b/services/static-webserver/client/source/class/osparc/ui/basic/Tag.js
index 4b23fc0efde..64930674e25 100644
--- a/services/static-webserver/client/source/class/osparc/ui/basic/Tag.js
+++ b/services/static-webserver/client/source/class/osparc/ui/basic/Tag.js
@@ -13,17 +13,19 @@ qx.Class.define("osparc.ui.basic.Tag", {
extend: qx.ui.basic.Label,
/**
* Constructor for the Tag element.
- * @param {String} value Short text to be shown on the tag
- * @param {String} color Color for the background, must be in hex3 or hex6 form
+ * @param {osparc.data.model.Tag} tag Short text to be shown on the tag
* @param {String} [filterGroupId] If present, clicking on the tab will dispatch a bus message with the
* id ``GroupIdTagsTrigger`` to be subscribed by a filter.
*/
- construct: function(value, color, filterGroupId) {
- this.base(arguments, value);
- this.setFont("text-11");
- if (color) {
- this.setColor(color);
+ construct: function(tag, filterGroupId) {
+ this.base(arguments);
+
+ if (tag) {
+ tag.bind("name", this, "value");
+ tag.bind("color", this, "color");
}
+ this.setFont("text-11");
+
if (filterGroupId) {
this.setCursor("pointer");
this.addListener("tap", e => {