From 7e1396af3a67781412fedb6160e5712ab66b733f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 10:16:35 +0200 Subject: [PATCH 01/59] searchFiles --- .../client/source/class/osparc/data/Resources.js | 4 ++++ .../client/source/class/osparc/store/Data.js | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index aa2d1de06535..59d9d2aba8f9 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -1377,6 +1377,10 @@ qx.Class.define("osparc.data.Resources", { method: "POST", url: statics.API + "/storage/locations/{locationId}:export-data" }, + searchFiles: { + method: "POST", + url: statics.API + "/storage/locations/{locationId}:search" + }, batchDelete: { method: "POST", url: statics.API + "/storage/locations/{locationId}/-/paths:batchDelete" diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index a1fd4f47add1..3c28d62fadb3 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -250,6 +250,18 @@ qx.Class.define("osparc.store.Data", { return osparc.data.Resources.fetch("storagePaths", "multiDownload", params); }, + searchFiles: function(searchText) { + const params = { + url: { + locationId: 0, + }, + data: { + searchText, + } + }; + return osparc.data.Resources.fetch("storagePaths", "searchFiles", params); + }, + deleteFiles: function(paths) { if (!osparc.data.Permissions.getInstance().canDo("study.node.data.delete", true)) { return null; From 905fa1bff85dc32ea0cfc2a1d399e6da44f8450d Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 10:28:51 +0200 Subject: [PATCH 02/59] extra info --- .../client/source/class/osparc/wrapper/BookACallIframe.js | 4 ++++ .../client/source/class/osparc/wrapper/RocketPreview.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/wrapper/BookACallIframe.js b/services/static-webserver/client/source/class/osparc/wrapper/BookACallIframe.js index 5f674a3e0304..17354f11104d 100644 --- a/services/static-webserver/client/source/class/osparc/wrapper/BookACallIframe.js +++ b/services/static-webserver/client/source/class/osparc/wrapper/BookACallIframe.js @@ -39,6 +39,10 @@ qx.Class.define("osparc.wrapper.BookACallIframe", { }, statics: { + NAME: "easy!appointments", + VERSION: "1.5.2", + URL: "https://easyappointments.org/", + DEV_SERVICE_URL: "http://10.43.103.145/index.php", }, diff --git a/services/static-webserver/client/source/class/osparc/wrapper/RocketPreview.js b/services/static-webserver/client/source/class/osparc/wrapper/RocketPreview.js index 668b7195312c..3ddd243ab335 100644 --- a/services/static-webserver/client/source/class/osparc/wrapper/RocketPreview.js +++ b/services/static-webserver/client/source/class/osparc/wrapper/RocketPreview.js @@ -48,6 +48,10 @@ qx.Class.define("osparc.wrapper.RocketPreview", { }, statics: { + NAME: "Sim4Life", + VERSION: "latest", + URL: "https://sim4life.swiss/", + INDEX_HTML: "rocketPreview/build/index.html", /** From c1dffbb039bb310a788835f193168da99654c2f9 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 10:31:06 +0200 Subject: [PATCH 03/59] define contexts --- .../source/class/osparc/dashboard/SearchBarFilterExtended.js | 3 ++- .../client/source/class/osparc/dashboard/StudyBrowser.js | 1 + .../static-webserver/client/source/class/osparc/store/Store.js | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index 5f127fc8fd46..84caa5d99c64 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -53,7 +53,8 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { "searchProjects", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS, "searchTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_TEMPLATES, "searchPublicTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES, - "searchFunctions" // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS + "searchFunctions", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS, + "searchFiles", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES, ], init: null, nullable: false, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index d2ead1e53bf1..735d165b2d66 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -75,6 +75,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { "searchTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_TEMPLATES, "searchPublicTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES, "searchFunctions", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS, + "searchFiles", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES, ], nullable: false, init: "studiesAndFolders", diff --git a/services/static-webserver/client/source/class/osparc/store/Store.js b/services/static-webserver/client/source/class/osparc/store/Store.js index b421d301c1ec..c71816377da0 100644 --- a/services/static-webserver/client/source/class/osparc/store/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Store.js @@ -87,6 +87,7 @@ qx.Class.define("osparc.store.Store", { "searchTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_TEMPLATES, "searchPublicTemplates", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES, "searchFunctions", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS, + "searchFiles", // osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES, ], init: "studiesAndFolders", nullable: false, From 729c2c9f08f10bd3f4bdc390c11e6fc908fc4625 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 10:38:48 +0200 Subject: [PATCH 04/59] Files in dropdown --- .../dashboard/ResourceContainerManager.js | 3 +++ .../dashboard/SearchBarFilterExtended.js | 20 +++++++++++++++++++ .../class/osparc/dashboard/StudyBrowser.js | 4 ++++ .../osparc/dashboard/StudyBrowserHeader.js | 5 +++++ 4 files changed, 32 insertions(+) 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 8ff84ed5bce2..10a35cc0ce45 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -166,6 +166,9 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS: text = this.tr("No Functions found"); break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + text = this.tr("No Files found"); + break; } break; } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index 84caa5d99c64..a42662073186 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -153,6 +153,16 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { contextDropDown.add(control); break; } + case "files-button": { + control = this.self().createListItem( + this.tr("Files"), + "@MaterialIcons/file/18", + "files" + ); + const contextDropDown = this.getChildControl("context-drop-down"); + contextDropDown.add(control); + break; + } case "filter-buttons": control = new qx.ui.toolbar.ToolBar().set({ backgroundColor: osparc.dashboard.SearchBarFilter.BG_COLOR, @@ -187,6 +197,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { if (osparc.product.Utils.showFunctions()) { this.getChildControl("functions-button"); } + this.getChildControl("files-button"); if (contextDropDown.getChildren().length === 1) { contextDropDown.hide(); } @@ -207,6 +218,9 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { case "functions": this.setCurrentContext(osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS); break; + case "files": + this.setCurrentContext(osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES); + break; } } }); @@ -286,6 +300,12 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { sharedWithButton.setVisibility("excluded"); tagsButton.setVisibility("excluded"); break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + contextDropDown.setSelection([this.getChildControl("files-button")]); + searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Files")); + sharedWithButton.setVisibility("excluded"); + tagsButton.setVisibility("excluded"); + break; } }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 735d165b2d66..dab26b2b3a28 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -59,6 +59,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { SEARCH_TEMPLATES: "searchTemplates", SEARCH_PUBLIC_TEMPLATES: "searchPublicTemplates", SEARCH_FUNCTIONS: "searchFunctions", + SEARCH_FILES: "searchFiles", } }, @@ -921,6 +922,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS: requestParams.type = "user"; break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + // nothing to add + break; } if (this.getCurrentContext().includes("search")) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js index 421479da04c6..acd6c79d9512 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js @@ -337,6 +337,11 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS: this.__setIcon("@FontAwesome5Solid/search/24"); title.setValue(this.tr("Functions results")); + break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + this.__setIcon("@FontAwesome5Solid/search/24"); + title.setValue(this.tr("Files results")); + break; } }, From 829f8f8e3193d7911e891273a982369891f2c717 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 10:42:19 +0200 Subject: [PATCH 05/59] files icon --- .../source/class/osparc/dashboard/SearchBarFilterExtended.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index a42662073186..23ff003c33d6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -146,7 +146,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { case "functions-button": { control = this.self().createListItem( this.tr("Functions"), - "@MaterialIcons/functions/18", + "@MaterialIcons/functions/16", "functions" ); const contextDropDown = this.getChildControl("context-drop-down"); @@ -156,7 +156,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { case "files-button": { control = this.self().createListItem( this.tr("Files"), - "@MaterialIcons/file/18", + "@FontAwesome5Solid/file-alt/14", "files" ); const contextDropDown = this.getChildControl("context-drop-down"); From f0146b696c41c34a6af79fe3f74ca10041473f21 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 11:21:21 +0200 Subject: [PATCH 06/59] __filesList --- .../class/osparc/dashboard/StudyBrowser.js | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index dab26b2b3a28..adfa170f9e58 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -115,8 +115,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __sortByButton: null, __workspacesList: null, __foldersList: null, - __loadingFolders: null, + __filesList: null, __loadingWorkspaces: null, + __loadingFolders: null, + __loadingFiles: null, __lastUrlParams: null, // overridden @@ -412,6 +414,27 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, + __reloadFiles: function() { + if ( + !osparc.auth.Manager.getInstance().isLoggedIn() || + osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES !== this.getCurrentContext() || + this.__loadingFiles + ) { + return; + } + + this.__loadingFiles = true; + this.__setFilesToList([]); + const filterData = this._searchBarFilter.getFilterData(); + const text = filterData.text ? encodeURIComponent(filterData.text) : ""; + osparc.store.Data.getInstance().searchFiles(text) + .then(files => { + this.__setFilesToList(files); + }) + .catch(console.error) + .finally(() => this.__loadingFiles = null); + }, + __resetStudiesList: function() { this._resourcesList = []; // It will remove the study cards @@ -457,6 +480,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadWorkspaceCards(); }, + __setFilesToList: function(files) { + this.__filesList = files; + files.forEach(file => file["resourceType"] = "file"); + this.__reloadFileCards(); + }, + _reloadCards: function() { const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false; const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; @@ -698,6 +727,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, // /FOLDERS + // FILES + __reloadFileCards: function() { + this._resourcesContainer.setFilesToList(this.__filesList); + this._resourcesContainer.reloadFiles(); + }, + // /FILES + __configureStudyCards: function(cards) { cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); @@ -1383,6 +1419,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // reset lists this.__setWorkspacesToList([]); this.__setFoldersToList([]); + this.__setFilesToList([]); this._resourcesList = []; this._resourcesContainer.setResourcesToList(this._resourcesList); this._resourcesContainer.reloadCards("studies"); @@ -1450,6 +1487,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.invalidateFunctions(); this.__reloadStudies(); break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + this._searchBarFilter.resetFilters(); + this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search Files"); + // Files can't be sorted and don't support list view + this._toolbar.exclude(); + this.__reloadFiles(); + break; case osparc.dashboard.StudyBrowser.CONTEXT.TRASH: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); From 224005533f514481b1a8f05cb89b1088ad57acba Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 11:49:15 +0200 Subject: [PATCH 07/59] FileButtonItem --- .../class/osparc/dashboard/FileButtonItem.js | 161 ++++++++++++++++++ .../dashboard/ResourceContainerManager.js | 33 ++++ .../class/osparc/dashboard/StudyBrowser.js | 7 + 3 files changed, 201 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js new file mode 100644 index 000000000000..d6ad991f6aca --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -0,0 +1,161 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * Widget used for displaying a File in the Study Browser + * + */ + +qx.Class.define("osparc.dashboard.FileButtonItem", { + extend: osparc.dashboard.FolderButtonBase, + + /** + * @param file {osparc.data.model.File} The file to display + */ + construct: function(file) { + this.base(arguments); + + this.set({ + appearance: "pb-study", // change this, + cursor: "auto", + }); + + this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); + + this.set({ + folder: folder + }); + }, + + events: { + "openLocation": "qx.event.type.Data", + }, + + properties: { + file: { + check: "osparc.data.model.File", + nullable: false, + init: null, + apply: "__applyFile", + }, + + title: { + check: "String", + nullable: true, + apply: "__applyTitle" + }, + + lastModified: { + check: "Date", + nullable: true, + apply: "__applyLastModified" + }, + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "icon": { + control = new osparc.dashboard.FolderWithSharedIcon().set({ + anonymous: true, + height: 40, + padding: 5 + }); + this._add(control, osparc.dashboard.FolderButtonBase.POS.ICON); + break; + } + case "title": + control = new qx.ui.basic.Label().set({ + font: "text-14", + }); + this._add(control, osparc.dashboard.FolderButtonBase.POS.TITLE); + break; + case "date-by": + control = new osparc.ui.basic.DateAndBy(); + this._add(control, osparc.dashboard.FolderButtonBase.POS.SUBTITLE); + break; + case "menu-button": { + control = new qx.ui.form.MenuButton().set({ + appearance: "form-button-outlined", + padding: [0, 8], + maxWidth: osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS, + maxHeight: osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS, + icon: "@FontAwesome5Solid/ellipsis-v/14", + focusable: false + }); + // make it circular + control.getContentElement().setStyles({ + "border-radius": `${osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS / 2}px` + }); + osparc.utils.Utils.setIdToWidget(control, "folderItemMenuButton"); + this._add(control, osparc.dashboard.FolderButtonBase.POS.MENU); + break; + } + } + return control || this.base(arguments, id); + }, + + __applyFile: function(file) { + this.getChildControl("icon"); + this.set({ + cardKey: "file-" + file.getFileId() + }); + file.bind("name", this, "title"); + file.bind("lastModified", this, "lastModified"); + + osparc.utils.Utils.setIdToWidget(this, "fileItem_" + file.getFileId()); + + this.__addMenuButton(); + }, + + __applyTitle: function(value) { + const label = this.getChildControl("title"); + label.set({ + value, + toolTipText: value, + }); + }, + + __applyLastModified: function(value) { + if (value) { + const dateBy = this.getChildControl("date-by"); + dateBy.set({ + date: value, + toolTipText: this.tr("Last modified"), + }) + } + }, + + __addMenuButton: function() { + const menuButton = this.getChildControl("menu-button"); + menuButton.setVisibility("visible"); + + const menu = new qx.ui.menu.Menu().set({ + appearance: "menu-wider", + position: "bottom-right", + }); + + const openLocationButton = new qx.ui.menu.Button(this.tr("Open location"), "@FontAwesome5Solid/folder/12"); + openLocationButton.addListener("execute", () => this.fireDataEvent("openLocation", this.getFolderId()), this); + osparc.utils.Utils.setIdToWidget(openLocationButton, "openLocationMenuItem"); + menu.add(openLocationButton); + + menuButton.setMenu(menu); + }, + } +}); 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 10a35cc0ce45..038eb4cd96ba 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -133,10 +133,12 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { members: { __foldersList: null, __workspacesList: null, + __filesList: null, __resourcesList: null, __groupedContainersList: null, __foldersContainer: null, __workspacesContainer: null, + __filesContainer: null, __nonGroupedContainer: null, __groupedContainers: null, __resourceType: null, @@ -529,6 +531,37 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { }, // /FOLDERS + // FILES + setFilesToList: function(filesList) { + this.__filesList = filesList; + }, + + reloadFolders: function() { + if (this.__filesContainer) { + this.__filesContainer.removeAll(); + this.__filesContainer.exclude(); + } + let fileCards = []; + this.__filesList.forEach(fileData => fileCards.push(this.__fileToCard(fileData))); + return fileCards; + }, + + __fileToCard: function(fileData) { + const card = this.__createFileCard(fileData); + this.__filesContainer.add(card); + this.__filesContainer.show(); + return card; + }, + + __createFileCard: function(file) { + const card = new osparc.dashboard.FileButtonItem(file); + [ + "openLocation", + ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); + return card; + }, + // /FILES + __moveNoGroupToLast: function() { const idx = this.__groupedContainers.getChildren().findIndex(grpContainer => grpContainer === this.__getGroupContainer("no-group")); if (idx > -1) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index adfa170f9e58..b69cd7332947 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1368,6 +1368,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { backToContext = osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS; } break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + if (isSearchContext) { + searchContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES; + } else { + backToContext = osparc.dashboard.StudyBrowser.CONTEXT.PROJECTS; + } + break; default: if (isSearchContext) { searchContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS; From fa41e862089f501730f3405edeef0bb6223572fe Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 11:51:27 +0200 Subject: [PATCH 08/59] minor --- .../source/class/osparc/dashboard/ResourceContainerManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 038eb4cd96ba..0ce4dc20cc27 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -536,7 +536,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__filesList = filesList; }, - reloadFolders: function() { + reloadFiles: function() { if (this.__filesContainer) { this.__filesContainer.removeAll(); this.__filesContainer.exclude(); From 55bbb2c15e2d01d2f103d948b9de79afe739907c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 12:34:36 +0200 Subject: [PATCH 09/59] FileButtonBase --- .../class/osparc/dashboard/FileButtonBase.js | 127 ++++++++++++++++++ .../class/osparc/dashboard/FileButtonItem.js | 4 +- 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js new file mode 100644 index 000000000000..557e08eb5876 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js @@ -0,0 +1,127 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.FileButtonBase", { + extend: qx.ui.core.Widget, + implement: [qx.ui.form.IModel, osparc.filter.IFilterable], + include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], + type: "abstract", + + construct: function() { + this.base(arguments); + + this.set({ + width: osparc.dashboard.GridButtonBase.ITEM_WIDTH, + minHeight: this.self().HEIGHT, + padding: 5, + alignY: "middle" + }); + + const gridLayout = new qx.ui.layout.Grid(); + gridLayout.setSpacing(this.self().SPACING); + gridLayout.setColumnFlex(this.self().POS.TITLE.column, 1); + gridLayout.setColumnAlign(this.self().POS.ICON.column, "center", "middle"); + gridLayout.setColumnAlign(this.self().POS.TITLE.column, "left", "middle"); + this._setLayout(gridLayout); + + this.addListener("pointerover", this.__onPointerOver, this); + this.addListener("pointerout", this.__onPointerOut, this); + }, + + properties: { + cardKey: { + check: "String", + nullable: true + }, + + resourceType: { + check: ["file"], + init: "file", + nullable: false + }, + + priority: { + check: "Number", + init: null, + nullable: false + } + }, + + statics: { + HEIGHT: 50, + SPACING: 5, + POS: { + ICON: { + column: 0, + row: 0, + rowSpan: 2 + }, + TITLE: { + column: 1, + row: 0 + }, + SUBTITLE: { + column: 1, + row: 1 + }, + MENU: { + column: 2, + row: 0, + rowSpan: 2 + } + }, + }, + + members: { // eslint-disable-line qx-rules/no-refs-in-members + // overridden + _forwardStates: { + focused : true, + hovered : true, + selected : true, + dragover : true + }, + + __onPointerOver: function() { + this.addState("hovered"); + }, + + __onPointerOut : function() { + this.removeState("hovered"); + }, + + _filter: function() { + this.exclude(); + }, + + _unfilter: function() { + this.show(); + }, + + _shouldApplyFilter: function(data) { + return false; + }, + + _shouldReactToFilter: function(data) { + return false; + } + }, + + destruct: function() { + this.removeListener("pointerover", this.__onPointerOver, this); + this.removeListener("pointerout", this.__onPointerOut, this); + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index d6ad991f6aca..709c3adb2506 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -21,7 +21,7 @@ */ qx.Class.define("osparc.dashboard.FileButtonItem", { - extend: osparc.dashboard.FolderButtonBase, + extend: osparc.dashboard.FileButtonBase, /** * @param file {osparc.data.model.File} The file to display @@ -37,7 +37,7 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); this.set({ - folder: folder + file: file }); }, From 5aa1719b8fa586c39409879ec32cda0a410ea9b2 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 13:57:30 +0200 Subject: [PATCH 10/59] request reaches the backend --- .../source/class/osparc/dashboard/StudyBrowser.js | 5 ++++- .../client/source/class/osparc/store/Data.js | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index b69cd7332947..fc522deae6eb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1263,6 +1263,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS: curatedContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS; break; + case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: + curatedContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES; + break; default: curatedContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS; break; @@ -1293,6 +1296,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_TEMPLATES, osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES, osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS, + osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES, ].includes(searchContext)) { this._changeContext(searchContext); } @@ -1495,7 +1499,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadStudies(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: - this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search Files"); // Files can't be sorted and don't support list view this._toolbar.exclude(); diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index 3c28d62fadb3..7ad7c4aefcde 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -250,15 +250,22 @@ qx.Class.define("osparc.store.Data", { return osparc.data.Resources.fetch("storagePaths", "multiDownload", params); }, - searchFiles: function(searchText) { + searchFiles: function(searchText, modifiedAtFrom = null, modifiedAtTo = null) { const params = { url: { locationId: 0, }, data: { - searchText, + filters: { + namePattern: searchText + } } }; + if (modifiedAtFrom && modifiedAtFrom) { + // OM todo + params.data["filters"]["modifiedAtFrom"] = modifiedAtFrom; + params.data["filters"]["modifiedAtTo"] = modifiedAtTo; + } return osparc.data.Resources.fetch("storagePaths", "searchFiles", params); }, From 403ea36c3324e23acf41baeb43a47a62b05a06f5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 14:14:02 +0200 Subject: [PATCH 11/59] streamHref --- .../class/osparc/dashboard/StudyBrowser.js | 14 ++++-- .../source/class/osparc/data/PollTask.js | 45 +++++++++++++++++-- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index fc522deae6eb..617b36aeb111 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -427,11 +427,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__setFilesToList([]); const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - osparc.store.Data.getInstance().searchFiles(text) - .then(files => { - this.__setFilesToList(files); + + const searchFilesPromise = osparc.store.Data.getInstance().searchFiles(text); + const pollTasks = osparc.store.PollTasks.getInstance(); + pollTasks.createPollingTask(searchFilesPromise) + .then(task => { + task.addListener("resultReceived", e => { + const files = e.getData(); + this.__setFilesToList(files); + }, this); }) - .catch(console.error) + .catch(err => console.log(err)) .finally(() => this.__loadingFiles = null); }, diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index cf6194233feb..c6440665093b 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -32,7 +32,8 @@ qx.Class.define("osparc.data.PollTask", { taskId: taskData["task_id"], taskName: taskData["task_name"] || "", statusHref: taskData["status_href"], - resultHref: taskData["result_href"], + resultHref: taskData["result_href"] || null, + streamHref: taskData["stream_href"] || null, }); if ("abort_href" in taskData) { @@ -77,7 +78,12 @@ qx.Class.define("osparc.data.PollTask", { resultHref: { check: "String", - nullable: false + nullable: true + }, + + streamHref: { + check: "String", + nullable: true }, abortHref: { @@ -92,7 +98,7 @@ qx.Class.define("osparc.data.PollTask", { nullable: false, init: false, event: "changeDone", - apply: "__fetchResults" + apply: "__fetchOutcome" } }, @@ -164,6 +170,16 @@ qx.Class.define("osparc.data.PollTask", { }); }, + __fetchOutcome: function() { + if (this.getDone()) { + if (this.getResultHref()) { + this.__fetchResults(); + } else if (this.getStreamHref()) { + this.__fetchStream(); + } + } + }, + __fetchResults: function() { if (this.isDone()) { const resultPath = this.self().extractPathname(this.getResultHref()); @@ -187,6 +203,29 @@ qx.Class.define("osparc.data.PollTask", { } }, + __fetchStream: function() { + if (this.isDone()) { + const streamPath = this.self().extractPathname(this.getStreamHref()); + fetch(streamPath) + .then(res => res.json()) + .then(result => { + if ("error" in result && result["error"]) { + throw result["error"]; + } + if ("data" in result && result["data"]) { + const resultData = result["data"]; + this.fireDataEvent("resultReceived", resultData); + return; + } + throw new Error("Missing stream data"); + }) + .catch(err => { + this.fireDataEvent("pollingError", err); + throw err; + }); + } + }, + abortRequested: function() { const abortHref = this.getAbortHref(); if (abortHref) { From ebeef3a91359ccc39491bdecced3c0ebeb851556 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 14:16:15 +0200 Subject: [PATCH 12/59] streamData --- .../client/source/class/osparc/data/PollTask.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index c6440665093b..768a90c0d7a4 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -207,13 +207,12 @@ qx.Class.define("osparc.data.PollTask", { if (this.isDone()) { const streamPath = this.self().extractPathname(this.getStreamHref()); fetch(streamPath) - .then(res => res.json()) - .then(result => { - if ("error" in result && result["error"]) { - throw result["error"]; + .then(streamData => { + if ("error" in streamData && streamData["error"]) { + throw streamData["error"]; } - if ("data" in result && result["data"]) { - const resultData = result["data"]; + if ("data" in streamData && streamData["data"]) { + const resultData = streamData["data"]; this.fireDataEvent("resultReceived", resultData); return; } From 57ecf6390b8a0ab296e1ae6c570989c35585e260 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 14:34:31 +0200 Subject: [PATCH 13/59] wildcard on my side --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 6 ++++-- .../client/source/class/osparc/store/Data.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 617b36aeb111..11d598115092 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -433,8 +433,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { pollTasks.createPollingTask(searchFilesPromise) .then(task => { task.addListener("resultReceived", e => { - const files = e.getData(); - this.__setFilesToList(files); + const streamData = e.getData(); + if ("items" in streamData) { + this.__setFilesToList(streamData["items"]); + } }, this); }) .catch(err => console.log(err)) diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index 7ad7c4aefcde..67f6e738210b 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -257,7 +257,7 @@ qx.Class.define("osparc.store.Data", { }, data: { filters: { - namePattern: searchText + namePattern: "*" + searchText + "*", } } }; From 52f4d7a0d4174be0e84da88708eef2eba5d55b34 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:25:45 +0200 Subject: [PATCH 14/59] _startPolling --- .../source/class/osparc/data/PollTask.js | 28 +++---- .../source/class/osparc/data/StreamTask.js | 76 +++++++++++++++++++ 2 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/data/StreamTask.js diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index 768a90c0d7a4..ae648cc80377 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -33,7 +33,6 @@ qx.Class.define("osparc.data.PollTask", { taskName: taskData["task_name"] || "", statusHref: taskData["status_href"], resultHref: taskData["result_href"] || null, - streamHref: taskData["stream_href"] || null, }); if ("abort_href" in taskData) { @@ -42,8 +41,7 @@ qx.Class.define("osparc.data.PollTask", { }); } - this.__retries = 3; - this.__pollTaskState(); + this._startPolling(); } }, @@ -78,12 +76,7 @@ qx.Class.define("osparc.data.PollTask", { resultHref: { check: "String", - nullable: true - }, - - streamHref: { - check: "String", - nullable: true + nullable: false }, abortHref: { @@ -98,7 +91,7 @@ qx.Class.define("osparc.data.PollTask", { nullable: false, init: false, event: "changeDone", - apply: "__fetchOutcome" + apply: "_applyDone", } }, @@ -131,6 +124,11 @@ qx.Class.define("osparc.data.PollTask", { __retries: null, __aborting: null, + _startPolling: function() { + this.__retries = 3; + this.__pollTaskState(); + }, + __pollTaskState: function() { const statusPath = this.self().extractPathname(this.getStatusHref()); fetch(statusPath) @@ -170,13 +168,9 @@ qx.Class.define("osparc.data.PollTask", { }); }, - __fetchOutcome: function() { - if (this.getDone()) { - if (this.getResultHref()) { - this.__fetchResults(); - } else if (this.getStreamHref()) { - this.__fetchStream(); - } + _applyDone: function(done) { + if (done && this.getResultHref()) { + this.__fetchResults(); } }, diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js new file mode 100644 index 000000000000..1eeea049109a --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -0,0 +1,76 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * @ignore(fetch) + */ + +qx.Class.define("osparc.data.StreamTask", { + extend: osparc.data.PollTask, + + construct: function(taskData, interval = 2000) { + this.set({ + streamHref: taskData["stream_href"] || null, + }); + + this.base(arguments, taskData, interval); + }, + + events: { + "streamReceived": "qx.event.type.Data", + }, + + properties: { + resultHref: { + refine: true, + nullable: true + }, + + streamHref: { + check: "String", + nullable: false, + }, + }, + + members: { + _startPolling: function() { + this.__fetchStream(); + }, + + __fetchStream: function() { + if (!this.isDone()) { + const streamPath = this.self().extractPathname(this.getStreamHref()); + fetch(streamPath) + .then(streamData => { + if ("error" in streamData && streamData["error"]) { + throw streamData["error"]; + } + if ("data" in streamData && streamData["data"]) { + const data = streamData["data"]; + this.fireDataEvent("streamReceived", data); + return; + } + throw new Error("Missing stream data"); + }) + .catch(err => { + this.fireDataEvent("pollingError", err); + throw err; + }); + } + }, + } +}); From 164be8b12935370f90f3aa66695898ef3d8c7b9e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:28:09 +0200 Subject: [PATCH 15/59] date-filters maybe --- .../dashboard/SearchBarFilterExtended.js | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index 23ff003c33d6..e1a36defc311 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -163,11 +163,15 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { contextDropDown.add(control); break; } + case "filters-layout": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)); + this._add(control); + break; case "filter-buttons": control = new qx.ui.toolbar.ToolBar().set({ backgroundColor: osparc.dashboard.SearchBarFilter.BG_COLOR, }); - this._add(control); + this.getChildControl("filters-layout").add(control); break; case "shared-with-button": control = new qx.ui.toolbar.MenuButton(this.tr("Shared with"), "@FontAwesome5Solid/share-alt/12"); @@ -179,6 +183,13 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { this.__addTagsMenu(control); this.getChildControl("filter-buttons").add(control); break; + case "date-filters": + control = new osparc.desktop.credits.DateFilters(); + control.addListener("change", e => { + console.log(e.getData()); + }); + this.getChildControl("filters-layout").add(control); + break; } return control || this.base(arguments, id); }, @@ -275,36 +286,42 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { const searchBarFilter = this.getChildControl("search-bar-filter"); const sharedWithButton = this.getChildControl("shared-with-button"); const tagsButton = this.getChildControl("tags-button"); + const dateFilters = this.getChildControl("date-filters"); switch (value) { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS: contextDropDown.setSelection([this.getChildControl("my-projects-button")]); searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in My projects")); - sharedWithButton.setVisibility("visible"); - tagsButton.setVisibility("visible"); + sharedWithButton.show(); + tagsButton.show(); + dateFilters.exclude(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_TEMPLATES: contextDropDown.setSelection([this.getChildControl("templates-button")]); searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Templates")); - sharedWithButton.setVisibility("excluded"); - tagsButton.setVisibility("visible"); + sharedWithButton.exclude(); + tagsButton.show(); + dateFilters.exclude(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES: contextDropDown.setSelection([this.getChildControl("public-projects-button")]); searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Public Projects")); - sharedWithButton.setVisibility("excluded"); - tagsButton.setVisibility("visible"); + sharedWithButton.exclude(); + tagsButton.show(); + dateFilters.exclude(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS: contextDropDown.setSelection([this.getChildControl("functions-button")]); searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Functions")); - sharedWithButton.setVisibility("excluded"); - tagsButton.setVisibility("excluded"); + sharedWithButton.exclude(); + tagsButton.exclude(); + dateFilters.exclude(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: contextDropDown.setSelection([this.getChildControl("files-button")]); searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Files")); - sharedWithButton.setVisibility("excluded"); - tagsButton.setVisibility("excluded"); + sharedWithButton.exclude(); + tagsButton.exclude(); + // dateFilters.show(); break; } }, From d788d03979064523b224b43c444789e147d4a220 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:31:53 +0200 Subject: [PATCH 16/59] minor --- .../osparc/desktop/credits/DateFilters.js | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js b/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js index 35d5e9d8dff1..637dac813b3e 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js @@ -10,8 +10,8 @@ qx.Class.define("osparc.desktop.credits.DateFilters", { construct() { this.base(arguments); - this._setLayout(new qx.ui.layout.HBox(7)); - this._buildLayout(); + this._setLayout(new qx.ui.layout.HBox(6)); + this.__buildLayout(); }, events: { @@ -19,7 +19,7 @@ qx.Class.define("osparc.desktop.credits.DateFilters", { }, members: { - _buildLayout() { + __buildLayout() { this._removeAll(); // Range defaults: today @@ -76,7 +76,7 @@ qx.Class.define("osparc.desktop.credits.DateFilters", { lastYear.setYear(today.getFullYear() - 1); this.__from.setValue(lastYear); this.__until.setValue(today); - }) + }); this._add(lastYearBtn); }, @@ -86,13 +86,13 @@ qx.Class.define("osparc.desktop.credits.DateFilters", { container.add(lbl); const datepicker = new qx.ui.form.DateField(); datepicker.setValue(initDate ? initDate : new Date()); - datepicker.addListener("changeValue", e => this._changeHandler(e)); + datepicker.addListener("changeValue", e => this.__changeHandler(e)); container.add(datepicker); this._add(container); return datepicker; }, - _changeHandler(e) { + __changeHandler: function(e) { const timestampFrom = this.__from.getValue().getTime(); const timestampUntil = this.__until.getValue().getTime(); if (timestampFrom > timestampUntil) { @@ -105,21 +105,17 @@ qx.Class.define("osparc.desktop.credits.DateFilters", { } return; } - const from = osparc.utils.Utils.formatDateYyyyMmDd(this.__from.getValue()); - const until = osparc.utils.Utils.formatDateYyyyMmDd(this.__until.getValue()); - this.fireDataEvent("change", { - from, - until - }); + const value = this.getValue(); + this.fireDataEvent("change", value); }, - getValue() { - const from = osparc.utils.Utils.formatDateYyyyMmDd(this.__from.getValue()) - const until = osparc.utils.Utils.formatDateYyyyMmDd(this.__until.getValue()) + getValue: function() { + const from = osparc.utils.Utils.formatDateYyyyMmDd(this.__from.getValue()); + const until = osparc.utils.Utils.formatDateYyyyMmDd(this.__until.getValue()); return { from, until - } + }; } } }); From 20791cef9264eea414ed1db75c3fd5890385bf92 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:45:14 +0200 Subject: [PATCH 17/59] working stream --- .../class/osparc/dashboard/StudyBrowser.js | 15 +++++------ .../source/class/osparc/data/PollTask.js | 24 +---------------- .../source/class/osparc/data/StreamTask.js | 27 ++++++++++++------- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 11d598115092..e8a1f6ec000d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -428,14 +428,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - const searchFilesPromise = osparc.store.Data.getInstance().searchFiles(text); - const pollTasks = osparc.store.PollTasks.getInstance(); - pollTasks.createPollingTask(searchFilesPromise) - .then(task => { - task.addListener("resultReceived", e => { - const streamData = e.getData(); - if ("items" in streamData) { - this.__setFilesToList(streamData["items"]); + osparc.store.Data.getInstance().searchFiles(text) + .then(streamData => { + const stream = new osparc.data.StreamTask(streamData); + stream.addListener("streamReceived", e => { + const data = e.getData(); + if ("items" in data) { + this.__setFilesToList(data["items"]); } }, this); }) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index ae648cc80377..cdc01f5c40d6 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -76,7 +76,7 @@ qx.Class.define("osparc.data.PollTask", { resultHref: { check: "String", - nullable: false + nullable: true }, abortHref: { @@ -197,28 +197,6 @@ qx.Class.define("osparc.data.PollTask", { } }, - __fetchStream: function() { - if (this.isDone()) { - const streamPath = this.self().extractPathname(this.getStreamHref()); - fetch(streamPath) - .then(streamData => { - if ("error" in streamData && streamData["error"]) { - throw streamData["error"]; - } - if ("data" in streamData && streamData["data"]) { - const resultData = streamData["data"]; - this.fireDataEvent("resultReceived", resultData); - return; - } - throw new Error("Missing stream data"); - }) - .catch(err => { - this.fireDataEvent("pollingError", err); - throw err; - }); - } - }, - abortRequested: function() { const abortHref = this.getAbortHref(); if (abortHref) { diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index 1eeea049109a..143b24a1b64f 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -22,12 +22,12 @@ qx.Class.define("osparc.data.StreamTask", { extend: osparc.data.PollTask, - construct: function(taskData, interval = 2000) { + construct: function(streamData, interval = 500) { this.set({ - streamHref: taskData["stream_href"] || null, + streamHref: streamData["stream_href"] || null, }); - this.base(arguments, taskData, interval); + this.base(arguments, streamData, interval); }, events: { @@ -35,11 +35,6 @@ qx.Class.define("osparc.data.StreamTask", { }, properties: { - resultHref: { - refine: true, - nullable: true - }, - streamHref: { check: "String", nullable: false, @@ -53,8 +48,17 @@ qx.Class.define("osparc.data.StreamTask", { __fetchStream: function() { if (!this.isDone()) { - const streamPath = this.self().extractPathname(this.getStreamHref()); + const streamPath = osparc.data.PollTask.extractPathname(this.getStreamHref()); fetch(streamPath) + .then(resp => { + if (resp.status === 200) { + return resp.json(); + } + const errMsg = qx.locale.Manager.tr("Unsuccessful streaming"); + const err = new Error(errMsg); + this.fireDataEvent("pollingError", err); + throw err; + }) .then(streamData => { if ("error" in streamData && streamData["error"]) { throw streamData["error"]; @@ -62,6 +66,11 @@ qx.Class.define("osparc.data.StreamTask", { if ("data" in streamData && streamData["data"]) { const data = streamData["data"]; this.fireDataEvent("streamReceived", data); + if ("end" in data && data["end"] === false) { + setTimeout(() => this.__fetchStream(), this.getPollInterval()); + } else { + this.setDone(true); + } return; } throw new Error("Missing stream data"); From 66775fc11736cbb338bc37d544a257148c034687 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:49:14 +0200 Subject: [PATCH 18/59] minor --- .../client/source/class/osparc/data/PollTask.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index cdc01f5c40d6..f28097c8a240 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -32,7 +32,7 @@ qx.Class.define("osparc.data.PollTask", { taskId: taskData["task_id"], taskName: taskData["task_name"] || "", statusHref: taskData["status_href"], - resultHref: taskData["result_href"] || null, + resultHref: taskData["result_href"], }); if ("abort_href" in taskData) { From ac6cb68ffc1060172984683b52734e1348b6b2c2 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 17:58:31 +0200 Subject: [PATCH 19/59] pageSize --- .../source/class/osparc/data/StreamTask.js | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index 143b24a1b64f..e96cbfe10e68 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -38,18 +38,33 @@ qx.Class.define("osparc.data.StreamTask", { streamHref: { check: "String", nullable: false, + init: null, + }, + + end: { + check: "Boolean", + nullable: false, + init: false, + }, + + pageSize: { + check: "Number", + nullable: false, + // init: 20, + init: 2, }, }, members: { _startPolling: function() { - this.__fetchStream(); + this.fetchStream(); }, - __fetchStream: function() { + fetchStream: function() { if (!this.isDone()) { const streamPath = osparc.data.PollTask.extractPathname(this.getStreamHref()); - fetch(streamPath) + const url = `${streamPath}?limit=${this.getPageSize()}`; + fetch(url) .then(resp => { if (resp.status === 200) { return resp.json(); @@ -64,11 +79,16 @@ qx.Class.define("osparc.data.StreamTask", { throw streamData["error"]; } if ("data" in streamData && streamData["data"]) { - const data = streamData["data"]; - this.fireDataEvent("streamReceived", data); - if ("end" in data && data["end"] === false) { - setTimeout(() => this.__fetchStream(), this.getPollInterval()); - } else { + const items = streamData["data"]["items"] || []; + const end = streamData["data"]["end"] || false; + if (items.length === 0 && end === false) { + // nothing to stream yet, try again later + setTimeout(() => this.fetchStream(), this.getPollInterval()); + return; + } + this.fireDataEvent("streamReceived", items); + if (end) { + this.setEnd(true); this.setDone(true); } return; From 06da80e14c704831468521f6324699ab29e54e6f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 18:01:58 +0200 Subject: [PATCH 20/59] fetchStream --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index e8a1f6ec000d..7dd716eb669b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -436,6 +436,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if ("items" in data) { this.__setFilesToList(data["items"]); } + if (stream.isEnd() === false) { + setTimeout(() => stream.fetchStream(), 2000); + } }, this); }) .catch(err => console.log(err)) From 90a908786ed7564d186bdb6539604c128b1c8239 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 15 Oct 2025 18:03:46 +0200 Subject: [PATCH 21/59] isEnd --- .../client/source/class/osparc/data/StreamTask.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index e96cbfe10e68..705d6dcf323b 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -61,7 +61,7 @@ qx.Class.define("osparc.data.StreamTask", { }, fetchStream: function() { - if (!this.isDone()) { + if (!this.isEnd()) { const streamPath = osparc.data.PollTask.extractPathname(this.getStreamHref()); const url = `${streamPath}?limit=${this.getPageSize()}`; fetch(url) @@ -89,7 +89,6 @@ qx.Class.define("osparc.data.StreamTask", { this.fireDataEvent("streamReceived", items); if (end) { this.setEnd(true); - this.setDone(true); } return; } From bab868df240bbbfdbb2332c1321fd3f9e6132d1e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 16 Oct 2025 10:07:56 +0200 Subject: [PATCH 22/59] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 7dd716eb669b..5de45d6f408e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -417,17 +417,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __reloadFiles: function() { if ( !osparc.auth.Manager.getInstance().isLoggedIn() || - osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES !== this.getCurrentContext() || - this.__loadingFiles + this.getCurrentContext() !== osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES || + this._loadingResourcesBtn.isFetching() ) { return; } - this.__loadingFiles = true; - this.__setFilesToList([]); + this._loadingResourcesBtn.setFetching(true); + this._loadingResourcesBtn.setVisibility("visible"); const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - osparc.store.Data.getInstance().searchFiles(text) .then(streamData => { const stream = new osparc.data.StreamTask(streamData); From 2e9fb64797ebe57688b1c1be94525ba6502b8cbc Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 16 Oct 2025 10:16:20 +0200 Subject: [PATCH 23/59] not needed --- .../client/source/class/osparc/store/PollTasks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/store/PollTasks.js b/services/static-webserver/client/source/class/osparc/store/PollTasks.js index 96752ecc5601..4d279b4e2bc2 100644 --- a/services/static-webserver/client/source/class/osparc/store/PollTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/PollTasks.js @@ -24,7 +24,6 @@ qx.Class.define("osparc.store.PollTasks", { check: "Array", init: [], nullable: true, - event: "changeTasks" } }, From 8cd467fbd0d3e9dddad6ccdfb5fc6e26d3e071e5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 16 Oct 2025 10:20:30 +0200 Subject: [PATCH 24/59] StreamTasks --- .../source/class/osparc/store/StreamTasks.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/store/StreamTasks.js diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js new file mode 100644 index 000000000000..4518c5012265 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -0,0 +1,75 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.store.StreamTasks", { + extend: qx.core.Object, + type: "singleton", + + properties: { + tasks: { + check: "Object", + init: {}, + nullable: false, + } + }, + + members: { + createStreamTask: function(streamPromise, interval) { + return new Promise((resolve, reject) => { + streamPromise + .then(streamData => { + if ("status_href" in streamData) { + const task = this.__addTask(streamData, interval); + resolve(task); + } else { + throw Error("Status missing"); + } + }) + .catch(err => reject(err)); + }); + }, + + __removeTask: function(task) { + const tasks = this.getTasks(); + const index = tasks.findIndex(t => t.getTaskId() === task.getTaskId()); + if (index > -1) { + tasks.splice(index, 1); + } + }, + + __addTask: function(streamData, interval) { + const tasks = this.getTasks(); + if (streamData["task_id"] in tasks) { + return tasks[streamData["task_id"]]; + } + + const stream = new osparc.data.StreamTask(streamData, interval); + stream.addListener("resultReceived", () => this.__removeTask(stream), this); + stream.addListener("taskAborted", () => this.__removeTask(stream), this); + tasks[stream.getTaskId()] = stream; + return stream; + }, + + fetchStream: function(taskId) { + const tasks = this.getTasks(); + if (taskId in tasks) { + const task = tasks[taskId]; + task.fetchStream(); + } + }, + } +}); From be860595150a8f92bc53e73721f5d5e8caa2f741 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 16 Oct 2025 10:21:22 +0200 Subject: [PATCH 25/59] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 5de45d6f408e..1c2b67b13091 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -427,9 +427,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._loadingResourcesBtn.setVisibility("visible"); const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - osparc.store.Data.getInstance().searchFiles(text) - .then(streamData => { - const stream = new osparc.data.StreamTask(streamData); + const streamPromise = osparc.store.Data.getInstance().searchFiles(text); + osparc.store.StreamTasks.getInstance().createStreamTask(streamPromise, 500) + .then(stream => { + // const stream = new osparc.data.StreamTask(streamData); stream.addListener("streamReceived", e => { const data = e.getData(); if ("items" in data) { From df68634ab3c8a9fc952d59b6ef4f21538c3d0b4e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 16 Oct 2025 17:42:50 +0200 Subject: [PATCH 26/59] minor --- .../client/source/class/osparc/support/ConversationPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/support/ConversationPage.js b/services/static-webserver/client/source/class/osparc/support/ConversationPage.js index 48474a66c84b..a5932ab0c3ee 100644 --- a/services/static-webserver/client/source/class/osparc/support/ConversationPage.js +++ b/services/static-webserver/client/source/class/osparc/support/ConversationPage.js @@ -68,7 +68,9 @@ qx.Class.define("osparc.support.ConversationPage", { backgroundColor: "transparent" }); control.addListener("execute", () => { - this.getConversation().setReadBy(true); + if (this.getConversation()) { + this.getConversation().setReadBy(true); + } this.setConversation(null); this.fireEvent("backToConversations"); }); From c02eb1d0f3e8ceea33b3c9d084647a0c3ab723fc Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 10:03:37 +0200 Subject: [PATCH 27/59] undoing --- .../client/source/class/osparc/data/PollTask.js | 8 +------- .../client/source/class/osparc/store/PollTasks.js | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index f28097c8a240..3b7be21d0432 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -91,7 +91,7 @@ qx.Class.define("osparc.data.PollTask", { nullable: false, init: false, event: "changeDone", - apply: "_applyDone", + apply: "__fetchResults", } }, @@ -168,12 +168,6 @@ qx.Class.define("osparc.data.PollTask", { }); }, - _applyDone: function(done) { - if (done && this.getResultHref()) { - this.__fetchResults(); - } - }, - __fetchResults: function() { if (this.isDone()) { const resultPath = this.self().extractPathname(this.getResultHref()); diff --git a/services/static-webserver/client/source/class/osparc/store/PollTasks.js b/services/static-webserver/client/source/class/osparc/store/PollTasks.js index 4d279b4e2bc2..96752ecc5601 100644 --- a/services/static-webserver/client/source/class/osparc/store/PollTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/PollTasks.js @@ -24,6 +24,7 @@ qx.Class.define("osparc.store.PollTasks", { check: "Array", init: [], nullable: true, + event: "changeTasks" } }, From 9057681692118106218f1b458b7c3bed65c856a7 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 10:04:04 +0200 Subject: [PATCH 28/59] minor --- .../client/source/class/osparc/data/PollTask.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index 3b7be21d0432..e00a8da36d5c 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -91,7 +91,7 @@ qx.Class.define("osparc.data.PollTask", { nullable: false, init: false, event: "changeDone", - apply: "__fetchResults", + apply: "__fetchResults" } }, From 3eecd9b0cb4dcf8048bb11c041922fed204972df Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 10:10:05 +0200 Subject: [PATCH 29/59] osparc.data.model.File --- .../dashboard/ResourceContainerManager.js | 3 +- .../source/class/osparc/data/model/File.js | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 services/static-webserver/client/source/class/osparc/data/model/File.js 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 0ce4dc20cc27..0b24b6d2df09 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -553,7 +553,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { return card; }, - __createFileCard: function(file) { + __createFileCard: function(fileData) { + const file = new osparc.data.model.File(fileData); const card = new osparc.dashboard.FileButtonItem(file); [ "openLocation", diff --git a/services/static-webserver/client/source/class/osparc/data/model/File.js b/services/static-webserver/client/source/class/osparc/data/model/File.js new file mode 100644 index 000000000000..cdd817ba77bf --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/model/File.js @@ -0,0 +1,92 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * Class that stores File data. + */ + +qx.Class.define("osparc.data.model.File", { + extend: qx.core.Object, + + /** + * @param fileData {Object} Object containing the serialized File Data + */ + construct: function(fileData) { + this.base(arguments); + + this.set({ + name: fileData.name, + projectId: fileData.projectId || null, + createdAt: new Date(fileData.createdAt), + modifiedAt: new Date(fileData.modifiedAt), + size: fileData.size, + path: fileData.path || null, + isDirectory: fileData.isDirectory || false, + }); + }, + + properties: { + name: { + check: "String", + nullable: false, + init: null, + event: "changeName" + }, + + projectId: { + check: "String", + nullable: null, + init: null, + event: "changeProjectId" + }, + + createdAt: { + check: "Date", + nullable: false, + init: null, + event: "changeCreatedAt" + }, + + modifiedAt: { + check: "Date", + nullable: false, + init: null, + event: "changeModifiedAt" + }, + + size: { + check: "Number", + nullable: true, + init: null, + event: "changeSize" + }, + + path: { + check: "String", + nullable: true, + init: null, + event: "changePath" + }, + + isDirectory: { + check: "Boolean", + nullable: false, + init: false, + event: "changeIsDirectory" + }, + }, +}); From a0064a3c301576f67b8035458c4c4a9cc7d5726f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 11:16:03 +0200 Subject: [PATCH 30/59] internalId --- .../class/osparc/dashboard/StudyBrowser.js | 13 +++--- .../source/class/osparc/store/StreamTasks.js | 44 ++++++++++++------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 1c2b67b13091..a5c5852ef2aa 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -118,7 +118,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __filesList: null, __loadingWorkspaces: null, __loadingFolders: null, - __loadingFiles: null, __lastUrlParams: null, // overridden @@ -428,7 +427,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; const streamPromise = osparc.store.Data.getInstance().searchFiles(text); - osparc.store.StreamTasks.getInstance().createStreamTask(streamPromise, 500) + osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text, streamPromise, 500) .then(stream => { // const stream = new osparc.data.StreamTask(streamData); stream.addListener("streamReceived", e => { @@ -436,13 +435,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if ("items" in data) { this.__setFilesToList(data["items"]); } - if (stream.isEnd() === false) { - setTimeout(() => stream.fetchStream(), 2000); - } }, this); }) .catch(err => console.log(err)) - .finally(() => this.__loadingFiles = null); + .finally(() => { + this._loadingResourcesBtn.setFetching(false); + if (stream.isEnd() === false) { + setTimeout(() => this.__reloadFiles(), 500); + } + }); }, __resetStudiesList: function() { diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js index 4518c5012265..7acdb98d8c33 100644 --- a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -28,12 +28,21 @@ qx.Class.define("osparc.store.StreamTasks", { }, members: { - createStreamTask: function(streamPromise, interval) { + getStreamTask: function(action, params, streamPromise, interval) { + const internalId = action + "_" + JSON.stringify(params); + const task = this.__getStreamTask(internalId); + if (task) { + return Promise.resolve(task); + } + return this.__createStreamTask(internalId, streamPromise, interval); + }, + + __createStreamTask: function(internalId, streamPromise, interval) { return new Promise((resolve, reject) => { streamPromise .then(streamData => { if ("status_href" in streamData) { - const task = this.__addTask(streamData, interval); + const task = this.__addStreamTask(internalId, streamData, interval); resolve(task); } else { throw Error("Status missing"); @@ -43,32 +52,33 @@ qx.Class.define("osparc.store.StreamTasks", { }); }, - __removeTask: function(task) { + __getStreamTask: function(internalId) { const tasks = this.getTasks(); - const index = tasks.findIndex(t => t.getTaskId() === task.getTaskId()); - if (index > -1) { - tasks.splice(index, 1); + if (internalId in tasks) { + return tasks[internalId]; } + return null; }, - __addTask: function(streamData, interval) { - const tasks = this.getTasks(); - if (streamData["task_id"] in tasks) { - return tasks[streamData["task_id"]]; + __addStreamTask: function(internalId, streamData, interval) { + const task = this.__getStreamTask(internalId); + if (task) { + return task; } const stream = new osparc.data.StreamTask(streamData, interval); - stream.addListener("resultReceived", () => this.__removeTask(stream), this); - stream.addListener("taskAborted", () => this.__removeTask(stream), this); - tasks[stream.getTaskId()] = stream; + stream.addListener("resultReceived", () => this.__removeStreamTask(stream), this); + stream.addListener("taskAborted", () => this.__removeStreamTask(stream), this); + const tasks = this.getTasks(); + tasks[internalId] = stream; return stream; }, - fetchStream: function(taskId) { + __removeStreamTask: function(stream) { const tasks = this.getTasks(); - if (taskId in tasks) { - const task = tasks[taskId]; - task.fetchStream(); + const index = tasks.findIndex(t => t.getTaskId() === stream.getTaskId()); + if (index > -1) { + tasks.splice(index, 1); } }, } From b2d2be439b647f23285b0ecdc451ee233fa7449c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 14:00:28 +0200 Subject: [PATCH 31/59] __reloadFiles --- .../class/osparc/dashboard/StudyBrowser.js | 31 ++++++--- .../source/class/osparc/data/StreamTask.js | 63 +++++++++---------- .../source/class/osparc/data/model/File.js | 8 +-- 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index a5c5852ef2aa..5370eb274a5f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -427,22 +427,33 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; const streamPromise = osparc.store.Data.getInstance().searchFiles(text); - osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text, streamPromise, 500) - .then(stream => { - // const stream = new osparc.data.StreamTask(streamData); - stream.addListener("streamReceived", e => { - const data = e.getData(); - if ("items" in data) { - this.__setFilesToList(data["items"]); - } - }, this); + let stream = null; + const pollingInterval = 2000; + osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text, streamPromise, pollingInterval) + .then(strm => { + stream = strm; + console.log("Streaming files search...", stream); + return stream.fetchStream() + }) + .then(streamData => { + const items = streamData["data"]["items"] || []; + if (items.length) { + this.__setFilesToList(items); + } + const end = streamData["data"]["end"] || false; + if (end === false && items.length === 0 && stream) { + // nothing to stream yet, try again later + setTimeout(() => this.__reloadFiles(), pollingInterval); + } }) .catch(err => console.log(err)) .finally(() => { this._loadingResourcesBtn.setFetching(false); - if (stream.isEnd() === false) { + /* + if (stream && stream.isEnd() === false) { setTimeout(() => this.__reloadFiles(), 500); } + */ }); }, diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index 705d6dcf323b..aef49cea0f8e 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -61,44 +61,39 @@ qx.Class.define("osparc.data.StreamTask", { }, fetchStream: function() { - if (!this.isEnd()) { - const streamPath = osparc.data.PollTask.extractPathname(this.getStreamHref()); - const url = `${streamPath}?limit=${this.getPageSize()}`; - fetch(url) - .then(resp => { - if (resp.status === 200) { - return resp.json(); - } - const errMsg = qx.locale.Manager.tr("Unsuccessful streaming"); - const err = new Error(errMsg); - this.fireDataEvent("pollingError", err); - throw err; - }) - .then(streamData => { - if ("error" in streamData && streamData["error"]) { - throw streamData["error"]; - } - if ("data" in streamData && streamData["data"]) { - const items = streamData["data"]["items"] || []; - const end = streamData["data"]["end"] || false; - if (items.length === 0 && end === false) { - // nothing to stream yet, try again later - setTimeout(() => this.fetchStream(), this.getPollInterval()); - return; + return new Promise((resolve, reject) => { + if (!this.isEnd()) { + const streamPath = osparc.data.PollTask.extractPathname(this.getStreamHref()); + const url = `${streamPath}?limit=${this.getPageSize()}`; + fetch(url) + .then(resp => { + if (resp.status === 200) { + return resp.json(); + } + const errMsg = qx.locale.Manager.tr("Unsuccessful streaming"); + const err = new Error(errMsg); + this.fireDataEvent("pollingError", err); + throw err; + }) + .then(streamData => { + if ("error" in streamData && streamData["error"]) { + throw streamData["error"]; } - this.fireDataEvent("streamReceived", items); + const end = streamData["data"]["end"] || false; if (end) { this.setEnd(true); } - return; - } - throw new Error("Missing stream data"); - }) - .catch(err => { - this.fireDataEvent("pollingError", err); - throw err; - }); - } + if ("data" in streamData && streamData["data"]) { + resolve(streamData); + } + throw new Error("Missing stream data"); + }) + .catch(err => { + this.fireDataEvent("pollingError", err); + reject(err); + }); + } + }); }, } }); diff --git a/services/static-webserver/client/source/class/osparc/data/model/File.js b/services/static-webserver/client/source/class/osparc/data/model/File.js index cdd817ba77bf..543b34b8bfb9 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/File.js +++ b/services/static-webserver/client/source/class/osparc/data/model/File.js @@ -33,8 +33,8 @@ qx.Class.define("osparc.data.model.File", { projectId: fileData.projectId || null, createdAt: new Date(fileData.createdAt), modifiedAt: new Date(fileData.modifiedAt), - size: fileData.size, - path: fileData.path || null, + size: fileData.size || null, + path: fileData.path, isDirectory: fileData.isDirectory || false, }); }, @@ -49,7 +49,7 @@ qx.Class.define("osparc.data.model.File", { projectId: { check: "String", - nullable: null, + nullable: true, init: null, event: "changeProjectId" }, @@ -77,7 +77,7 @@ qx.Class.define("osparc.data.model.File", { path: { check: "String", - nullable: true, + nullable: false, init: null, event: "changePath" }, From e75028b96356c615814f2525f686df9d5f35fd1c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 14:11:39 +0200 Subject: [PATCH 32/59] _startPolling --- .../client/source/class/osparc/data/StreamTask.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index aef49cea0f8e..da8e053ed8fb 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -56,8 +56,10 @@ qx.Class.define("osparc.data.StreamTask", { }, members: { + // override _startPolling: function() { - this.fetchStream(); + return; + // this.fetchStream(); }, fetchStream: function() { From 8442e4358359b762d3b102497f3695fe7c07c49d Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 14:50:20 +0200 Subject: [PATCH 33/59] improve promises logic --- .../source/class/osparc/store/StreamTasks.js | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js index 7acdb98d8c33..e298af18e8f8 100644 --- a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -32,32 +32,32 @@ qx.Class.define("osparc.store.StreamTasks", { const internalId = action + "_" + JSON.stringify(params); const task = this.__getStreamTask(internalId); if (task) { + console.log("Reusing existing stream task:", internalId); return Promise.resolve(task); } - return this.__createStreamTask(internalId, streamPromise, interval); + return this.__createStreamTask(internalId, streamPromise, interval) + .then(streamTask => { + console.log("Creating new stream task:", internalId); + return streamTask; + }) + .catch(err => Promise.reject(err)); }, __createStreamTask: function(internalId, streamPromise, interval) { - return new Promise((resolve, reject) => { - streamPromise - .then(streamData => { - if ("status_href" in streamData) { - const task = this.__addStreamTask(internalId, streamData, interval); - resolve(task); - } else { - throw Error("Status missing"); - } - }) - .catch(err => reject(err)); - }); + return streamPromise + .then(streamData => { + console.log("Stream data received:", streamData); + if (!("stream_href" in streamData)) { + throw new Error("Stream href missing"); + } + return this.__addStreamTask(internalId, streamData, interval); + }) + .catch(err => Promise.reject(err)); }, __getStreamTask: function(internalId) { const tasks = this.getTasks(); - if (internalId in tasks) { - return tasks[internalId]; - } - return null; + return tasks[internalId] || null; }, __addStreamTask: function(internalId, streamData, interval) { @@ -67,7 +67,7 @@ qx.Class.define("osparc.store.StreamTasks", { } const stream = new osparc.data.StreamTask(streamData, interval); - stream.addListener("resultReceived", () => this.__removeStreamTask(stream), this); + // stream.addListener("resultReceived", () => this.__removeStreamTask(stream), this); stream.addListener("taskAborted", () => this.__removeStreamTask(stream), this); const tasks = this.getTasks(); tasks[internalId] = stream; From d9f8054305ff7dcdb748500357a16ec517f561fa Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 15:15:27 +0200 Subject: [PATCH 34/59] refactor --- .../class/osparc/dashboard/StudyBrowser.js | 27 +++++++++------ .../source/class/osparc/data/PollTask.js | 2 ++ .../source/class/osparc/store/StreamTasks.js | 34 ++++++------------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 5370eb274a5f..532f425a5edb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -426,23 +426,28 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._loadingResourcesBtn.setVisibility("visible"); const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - const streamPromise = osparc.store.Data.getInstance().searchFiles(text); - let stream = null; - const pollingInterval = 2000; - osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text, streamPromise, pollingInterval) - .then(strm => { - stream = strm; - console.log("Streaming files search...", stream); - return stream.fetchStream() - }) + const existingStream = osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text); + if (existingStream) { + this.__fetchFilesFromStream(existingStream); + } else { + const streamPromise = osparc.store.Data.getInstance().searchFiles(text); + const pollingInterval = 2000; + osparc.store.StreamTasks.getInstance().createStreamTask("files_search", text, streamPromise, pollingInterval) + .then(newStream => this.__fetchFilesFromStream(newStream)); + } + }, + + __fetchFilesFromStream: function(stream) { + stream.fetchStream() .then(streamData => { const items = streamData["data"]["items"] || []; if (items.length) { this.__setFilesToList(items); } const end = streamData["data"]["end"] || false; - if (end === false && items.length === 0 && stream) { + if (end === false || items.length === 0) { // nothing to stream yet, try again later + const pollingInterval = 2000; setTimeout(() => this.__reloadFiles(), pollingInterval); } }) @@ -450,7 +455,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .finally(() => { this._loadingResourcesBtn.setFetching(false); /* - if (stream && stream.isEnd() === false) { + if (stream.isEnd() === false) { setTimeout(() => this.__reloadFiles(), 500); } */ diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index e00a8da36d5c..9d4ee57499da 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -131,6 +131,8 @@ qx.Class.define("osparc.data.PollTask", { __pollTaskState: function() { const statusPath = this.self().extractPathname(this.getStatusHref()); + // OM remove + return; fetch(statusPath) .then(resp => { if (this.__aborting || this.getDone()) { diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js index e298af18e8f8..a17b6e7a6aa9 100644 --- a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -27,45 +27,33 @@ qx.Class.define("osparc.store.StreamTasks", { } }, - members: { - getStreamTask: function(action, params, streamPromise, interval) { - const internalId = action + "_" + JSON.stringify(params); - const task = this.__getStreamTask(internalId); - if (task) { - console.log("Reusing existing stream task:", internalId); - return Promise.resolve(task); - } - return this.__createStreamTask(internalId, streamPromise, interval) - .then(streamTask => { - console.log("Creating new stream task:", internalId); - return streamTask; - }) - .catch(err => Promise.reject(err)); + statics: { + actionToInternalId: function(action, params) { + return action + "_" + JSON.stringify(params); }, + }, - __createStreamTask: function(internalId, streamPromise, interval) { + members: { + createStreamTask: function(action, params, streamPromise, interval) { return streamPromise .then(streamData => { console.log("Stream data received:", streamData); if (!("stream_href" in streamData)) { throw new Error("Stream href missing"); } - return this.__addStreamTask(internalId, streamData, interval); + return this.__addStreamTask(action, params, streamData, interval); }) .catch(err => Promise.reject(err)); }, - __getStreamTask: function(internalId) { + getStreamTask: function(action, params) { + const internalId = osparc.store.StreamTasks.actionToInternalId(action, params); const tasks = this.getTasks(); return tasks[internalId] || null; }, - __addStreamTask: function(internalId, streamData, interval) { - const task = this.__getStreamTask(internalId); - if (task) { - return task; - } - + __addStreamTask: function(action, params, streamData, interval) { + const internalId = osparc.store.StreamTasks.actionToInternalId(action, params); const stream = new osparc.data.StreamTask(streamData, interval); // stream.addListener("resultReceived", () => this.__removeStreamTask(stream), this); stream.addListener("taskAborted", () => this.__removeStreamTask(stream), this); From e4513a85567d84c79f30ca67c0164a8c51e4da10 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 16:22:58 +0200 Subject: [PATCH 35/59] minor --- .../source/class/osparc/data/model/File.js | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/File.js b/services/static-webserver/client/source/class/osparc/data/model/File.js index 543b34b8bfb9..b671232d897f 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/File.js +++ b/services/static-webserver/client/source/class/osparc/data/model/File.js @@ -30,12 +30,12 @@ qx.Class.define("osparc.data.model.File", { this.set({ name: fileData.name, - projectId: fileData.projectId || null, + projectId: fileData.projectId, + path: fileData.path, createdAt: new Date(fileData.createdAt), modifiedAt: new Date(fileData.modifiedAt), - size: fileData.size || null, - path: fileData.path, isDirectory: fileData.isDirectory || false, + size: fileData.size || null, }); }, @@ -49,11 +49,18 @@ qx.Class.define("osparc.data.model.File", { projectId: { check: "String", - nullable: true, + nullable: false, init: null, event: "changeProjectId" }, + path: { + check: "String", + nullable: false, + init: null, + event: "changePath" + }, + createdAt: { check: "Date", nullable: false, @@ -68,25 +75,18 @@ qx.Class.define("osparc.data.model.File", { event: "changeModifiedAt" }, - size: { - check: "Number", - nullable: true, - init: null, - event: "changeSize" - }, - - path: { - check: "String", - nullable: false, - init: null, - event: "changePath" - }, - isDirectory: { check: "Boolean", nullable: false, init: false, event: "changeIsDirectory" }, + + size: { + check: "Number", + nullable: true, + init: null, + event: "changeSize" + }, }, }); From 4cf2e2a9afbd8b31a2e7bd7e234fb6ef3c621b54 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 17 Oct 2025 17:24:58 +0200 Subject: [PATCH 36/59] more improvements --- .../class/osparc/dashboard/StudyBrowser.js | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 532f425a5edb..111ab8dc8380 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -179,7 +179,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, reloadMoreResources: function() { - this.__reloadStudies(); + if (this.getCurrentContext() === osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES) { + this.__reloadSearchFiles(); + } else { + this.__reloadStudies(); + } }, __reloadWorkspaces: function() { @@ -413,7 +417,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, - __reloadFiles: function() { + __reloadSearchFiles: function() { if ( !osparc.auth.Manager.getInstance().isLoggedIn() || this.getCurrentContext() !== osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES || @@ -427,6 +431,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; const existingStream = osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text); + // TODO: abort last stream if it changed if (existingStream) { this.__fetchFilesFromStream(existingStream); } else { @@ -445,20 +450,19 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__setFilesToList(items); } const end = streamData["data"]["end"] || false; - if (end === false || items.length === 0) { - // nothing to stream yet, try again later - const pollingInterval = 2000; - setTimeout(() => this.__reloadFiles(), pollingInterval); - } + stream.setEnd(end); + + // a bit hacky + this._resourcesContainer.getFlatList().nextRequest = !end; }) .catch(err => console.log(err)) .finally(() => { this._loadingResourcesBtn.setFetching(false); - /* - if (stream.isEnd() === false) { - setTimeout(() => this.__reloadFiles(), 500); + if (this._resourcesContainer.getFlatList()) { + this._loadingResourcesBtn.setVisibility(this._resourcesContainer.getFlatList().nextRequest ? "visible" : "excluded"); } - */ + // delay the next request to avoid flooding the server + setTimeout(() => this._moreResourcesRequired(), 100); }); }, @@ -515,7 +519,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _reloadCards: function() { const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false; - const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; + // const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; + const visibility = true; this._resourcesContainer.setResourcesToList(this._resourcesList); const cards = this._resourcesContainer.reloadCards("studies"); @@ -1069,6 +1074,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }, + invalidateFiles: function() { + // this.__resetStudiesList(); + if (this._resourcesContainer.getFlatList()) { + this._resourcesContainer.getFlatList().nextRequest = null; + } + }, + __addNewPlusButton: function() { const newPlusButton = new osparc.dashboard.NewPlusButton(); this._leftFilters.add(newPlusButton); @@ -1529,7 +1541,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search Files"); // Files can't be sorted and don't support list view this._toolbar.exclude(); - this.__reloadFiles(); + this._loadingResourcesBtn.setFetching(false); + this.invalidateFiles(); + this.__reloadSearchFiles(); break; case osparc.dashboard.StudyBrowser.CONTEXT.TRASH: this._searchBarFilter.resetFilters(); From bf0b98e618e9c76801cc811229eebec1a4503ea1 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Fri, 17 Oct 2025 20:42:36 +0200 Subject: [PATCH 37/59] yield items as they come --- .../src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py b/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py index d40a90b084c8..d43f3418c5bc 100644 --- a/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py +++ b/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py @@ -162,6 +162,7 @@ async def search( project_id=project_id, name_pattern=name_pattern, modified_at=modified_at, + limit=1, # NOTE: yield items as they come ): data = [ TaskStreamItem( From 3717edbcf81faf90e56d2e9139dd7ff78c4e0157 Mon Sep 17 00:00:00 2001 From: Giancarlo Romeo Date: Mon, 20 Oct 2025 11:25:51 +0200 Subject: [PATCH 38/59] don't push empty results --- .../simcore_service_storage/api/_worker_tasks/_simcore_s3.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py b/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py index 758295092e32..549774bb1026 100644 --- a/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py +++ b/services/storage/src/simcore_service_storage/api/_worker_tasks/_simcore_s3.py @@ -164,6 +164,9 @@ async def search( modified_at=modified_at, limit=1, # NOTE: yield items as they come ): + if not items: + continue + data = [ TaskStreamItem( data=SearchResultItem( From 6b3f9b396ee3e15196c0132787d91850b0127e02 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 11:35:27 +0200 Subject: [PATCH 39/59] reset files --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 111ab8dc8380..b0a03724cdef 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -764,6 +764,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesContainer.setFilesToList(this.__filesList); this._resourcesContainer.reloadFiles(); }, + + __resetFilesList: function() { + this._resourcesList = []; + // It will remove the file cards + this._reloadCards(); + }, // /FILES __configureStudyCards: function(cards) { @@ -1075,7 +1081,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, invalidateFiles: function() { - // this.__resetStudiesList(); + this.__resetFilesList(); if (this._resourcesContainer.getFlatList()) { this._resourcesContainer.getFlatList().nextRequest = null; } From 6ce1ef144c26d4554554856282ac0e3d7ffb5c36 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 13:03:37 +0200 Subject: [PATCH 40/59] RIGHT_BUTTON_POS --- .../class/osparc/navigation/NavigationBar.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js b/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js index 75657b36fe1f..da0fa30810e4 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js +++ b/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js @@ -90,6 +90,19 @@ qx.Class.define("osparc.navigation.NavigationBar", { minHeight: 30 }, + RIGHT_BUTTON_POS: { + AVATARS: 0, + EXPIRATION: 1, + TASKS: 2, + JOBS: 3, + NOTIFICATIONS: 4, + HELP: 5, + CREDITS: 6, + LOGIN: 7, + USER_MENU: 8, + USER_MENU_COMPACT: 9, + }, + RIGHT_BUTTON_OPTS: { cursor: "pointer", alignX: "center", @@ -259,7 +272,7 @@ qx.Class.define("osparc.navigation.NavigationBar", { alignY: "middle", visibility: "excluded", }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.AVATARS); break; } case "expiration-icon": { @@ -285,38 +298,38 @@ qx.Class.define("osparc.navigation.NavigationBar", { return "excluded"; } }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.EXPIRATION); break; } case "tasks-button": control = new osparc.task.TasksButton().set({ ...this.self().RIGHT_BUTTON_OPTS }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.TASKS); break; case "jobs-button": control = new osparc.jobs.JobsButton().set({ ...this.self().RIGHT_BUTTON_OPTS }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.JOBS); break; case "notifications-button": control = new osparc.notification.NotificationsButton().set({ ...this.self().RIGHT_BUTTON_OPTS }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.NOTIFICATIONS); break; case "help-button": control = new osparc.support.SupportButton().set({ ...this.self().RIGHT_BUTTON_OPTS }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.HELP); break; case "credits-button": control = new osparc.desktop.credits.CreditsIndicatorButton().set({ ...this.self().RIGHT_BUTTON_OPTS }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.CREDITS); break; case "log-in-button": { control = this.__createLoginBtn().set({ @@ -327,20 +340,20 @@ qx.Class.define("osparc.navigation.NavigationBar", { authData.bind("guest", control, "visibility", { converter: isGuest => isGuest ? "visible" : "excluded" }); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.LOGIN); break; } case "user-menu": control = new osparc.navigation.UserMenuButton(); control.populateMenu(); control.set(this.self().BUTTON_OPTIONS); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.USER_MENU); break; case "user-menu-compact": control = new osparc.navigation.UserMenuButton(); control.populateMenuCompact(); control.set(this.self().BUTTON_OPTIONS); - this.getChildControl("right-items").add(control); + this.getChildControl("right-items").addAt(control, this.self().RIGHT_BUTTON_POS.USER_MENU_COMPACT); break; } return control || this.base(arguments, id); From 1fa62138563154ac9e76f6629a1945b37d9dde26 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 13:03:53 +0200 Subject: [PATCH 41/59] fetching --- .../class/osparc/dashboard/StudyBrowser.js | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index b0a03724cdef..f80e45eef77c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -455,14 +455,30 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // a bit hacky this._resourcesContainer.getFlatList().nextRequest = !end; }) - .catch(err => console.log(err)) - .finally(() => { - this._loadingResourcesBtn.setFetching(false); + .catch(err => { + osparc.FlashMessenger.logError(err); + // stop fetching if (this._resourcesContainer.getFlatList()) { - this._loadingResourcesBtn.setVisibility(this._resourcesContainer.getFlatList().nextRequest ? "visible" : "excluded"); + this._resourcesContainer.getFlatList().nextRequest = null; + } + }) + .finally(() => { + if (this._resourcesContainer.getFlatList() && this._resourcesContainer.getFlatList().nextRequest) { + this._loadingResourcesBtn.set({ + visibility: "visible", + fetching: true + }); + // delay the next request to avoid flooding the server + setTimeout(() => { + this._loadingResourcesBtn.setFetching(false); // make it false before calling moreResourcesRequired + this._moreResourcesRequired(); + }, 2000); + } else if (this._loadingResourcesBtn) { + this._loadingResourcesBtn.set({ + visibility: "excluded", + fetching: false + }); } - // delay the next request to avoid flooding the server - setTimeout(() => this._moreResourcesRequired(), 100); }); }, @@ -519,8 +535,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _reloadCards: function() { const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false; - // const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; - const visibility = true; + const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; this._resourcesContainer.setResourcesToList(this._resourcesList); const cards = this._resourcesContainer.reloadCards("studies"); From b2d4078c14e619ee411f63252a4b0984b08ed8e5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 14:29:14 +0200 Subject: [PATCH 42/59] minor --- .../class/osparc/dashboard/FileButtonItem.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index 709c3adb2506..61ac3aae9aec 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -56,13 +56,13 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { title: { check: "String", nullable: true, - apply: "__applyTitle" + apply: "__applyTitle", }, - lastModified: { + modifiedAt: { check: "Date", nullable: true, - apply: "__applyLastModified" + apply: "__applyModifiedAt", }, }, @@ -111,14 +111,15 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { }, __applyFile: function(file) { + const id = file.getPath(); this.getChildControl("icon"); this.set({ - cardKey: "file-" + file.getFileId() + cardKey: "file-" + id, }); file.bind("name", this, "title"); - file.bind("lastModified", this, "lastModified"); + file.bind("modifiedAt", this, "modifiedAt"); - osparc.utils.Utils.setIdToWidget(this, "fileItem_" + file.getFileId()); + osparc.utils.Utils.setIdToWidget(this, "fileItem_" + id); this.__addMenuButton(); }, @@ -131,7 +132,7 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { }); }, - __applyLastModified: function(value) { + __applyModifiedAt: function(value) { if (value) { const dateBy = this.getChildControl("date-by"); dateBy.set({ From a70c0e7a516280558947af703ce441760376e34a Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 14:34:33 +0200 Subject: [PATCH 43/59] listing files --- .../client/source/class/osparc/dashboard/CardContainer.js | 3 ++- .../class/osparc/dashboard/ResourceContainerManager.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js index 281b16bfb4bc..76bae211e26d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js @@ -27,7 +27,8 @@ qx.Class.define("osparc.dashboard.CardContainer", { return ( widget instanceof osparc.dashboard.CardBase || widget instanceof osparc.dashboard.FolderButtonBase || - widget instanceof osparc.dashboard.WorkspaceButtonBase + widget instanceof osparc.dashboard.WorkspaceButtonBase || + widget instanceof osparc.dashboard.FileButtonBase ); }, }, 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 0b24b6d2df09..b0cc96956e7e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -29,6 +29,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__workspacesList = []; this.__foldersList = []; + this.__filesList = []; this.__resourcesList = []; this.__groupedContainersList = []; this.__resourceType = resourceType || "study"; @@ -41,6 +42,10 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { const foldersContainer = this.__foldersContainer = new osparc.dashboard.CardContainer(); this.__foldersContainer.exclude(); this._add(foldersContainer); + + const filesContainer = this.__filesContainer = new osparc.dashboard.CardContainer(); + this.__filesContainer.exclude(); + this._add(filesContainer); } const noResourcesFound = this.__noResourcesFound = new qx.ui.basic.Label("No resources found").set({ From 9c241d6a6368748288ed979f937e5b533d7097b9 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 14:45:03 +0200 Subject: [PATCH 44/59] listing files --- .../class/osparc/dashboard/StudyBrowser.js | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index f80e45eef77c..ce9fdf0d4e99 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -447,7 +447,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .then(streamData => { const items = streamData["data"]["items"] || []; if (items.length) { - this.__setFilesToList(items); + this.__addFilesToList(items); } const end = streamData["data"]["end"] || false; stream.setEnd(end); @@ -515,24 +515,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, - __setFoldersToList: function(folders) { - this.__foldersList = folders; - folders.forEach(folder => folder["resourceType"] = "folder"); - this.__reloadFolderCards(); - }, - - __setWorkspacesToList: function(workspaces) { - this.__workspacesList = workspaces; - workspaces.forEach(workspace => workspace["resourceType"] = "workspace"); - this.__reloadWorkspaceCards(); - }, - - __setFilesToList: function(files) { - this.__filesList = files; - files.forEach(file => file["resourceType"] = "file"); - this.__reloadFileCards(); - }, - _reloadCards: function() { const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false; const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded"; @@ -565,6 +547,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, // WORKSPACES + __setWorkspacesToList: function(workspaces) { + this.__workspacesList = workspaces; + workspaces.forEach(workspace => workspace["resourceType"] = "workspace"); + this.__reloadWorkspaceCards(); + }, + __reloadWorkspaceCards: function() { this._resourcesContainer.setWorkspacesToList(this.__workspacesList); this._resourcesContainer.reloadWorkspaces(); @@ -631,6 +619,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // /WORKSPACES // FOLDERS + __setFoldersToList: function(folders) { + this.__foldersList = folders; + folders.forEach(folder => folder["resourceType"] = "folder"); + this.__reloadFolderCards(); + }, + __reloadFolderCards: function() { this._resourcesContainer.setFoldersToList(this.__foldersList); this._resourcesContainer.reloadFolders(); @@ -775,6 +769,22 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // /FOLDERS // FILES + __setFilesToList: function(files) { + this.__filesList = files; + files.forEach(file => file["resourceType"] = "file"); + this.__reloadFileCards(); + }, + + __addFilesToList: function(filesList) { + filesList.forEach(fileData => { + const idx = this.__filesList.findIndex(fl => fl["path"] === fileData["path"]); + if (idx === -1) { + this.__filesList.push(fileData); + } + }); + this.__reloadFileCards(); + }, + __reloadFileCards: function() { this._resourcesContainer.setFilesToList(this.__filesList); this._resourcesContainer.reloadFiles(); From f1b15149db9d4b6f4d25fd0fc4e0b513bb7793ee Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 15:03:46 +0200 Subject: [PATCH 45/59] inherit from ListButtonBase --- .../class/osparc/dashboard/CardContainer.js | 2 +- .../class/osparc/dashboard/FileButtonBase.js | 127 ------------------ .../class/osparc/dashboard/FileButtonItem.js | 73 +++------- 3 files changed, 23 insertions(+), 179 deletions(-) delete mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js index 76bae211e26d..adeccdabf516 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js @@ -28,7 +28,7 @@ qx.Class.define("osparc.dashboard.CardContainer", { widget instanceof osparc.dashboard.CardBase || widget instanceof osparc.dashboard.FolderButtonBase || widget instanceof osparc.dashboard.WorkspaceButtonBase || - widget instanceof osparc.dashboard.FileButtonBase + widget instanceof osparc.dashboard.FileButtonItem ); }, }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js deleted file mode 100644 index 557e08eb5876..000000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonBase.js +++ /dev/null @@ -1,127 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2025 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Class.define("osparc.dashboard.FileButtonBase", { - extend: qx.ui.core.Widget, - implement: [qx.ui.form.IModel, osparc.filter.IFilterable], - include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], - type: "abstract", - - construct: function() { - this.base(arguments); - - this.set({ - width: osparc.dashboard.GridButtonBase.ITEM_WIDTH, - minHeight: this.self().HEIGHT, - padding: 5, - alignY: "middle" - }); - - const gridLayout = new qx.ui.layout.Grid(); - gridLayout.setSpacing(this.self().SPACING); - gridLayout.setColumnFlex(this.self().POS.TITLE.column, 1); - gridLayout.setColumnAlign(this.self().POS.ICON.column, "center", "middle"); - gridLayout.setColumnAlign(this.self().POS.TITLE.column, "left", "middle"); - this._setLayout(gridLayout); - - this.addListener("pointerover", this.__onPointerOver, this); - this.addListener("pointerout", this.__onPointerOut, this); - }, - - properties: { - cardKey: { - check: "String", - nullable: true - }, - - resourceType: { - check: ["file"], - init: "file", - nullable: false - }, - - priority: { - check: "Number", - init: null, - nullable: false - } - }, - - statics: { - HEIGHT: 50, - SPACING: 5, - POS: { - ICON: { - column: 0, - row: 0, - rowSpan: 2 - }, - TITLE: { - column: 1, - row: 0 - }, - SUBTITLE: { - column: 1, - row: 1 - }, - MENU: { - column: 2, - row: 0, - rowSpan: 2 - } - }, - }, - - members: { // eslint-disable-line qx-rules/no-refs-in-members - // overridden - _forwardStates: { - focused : true, - hovered : true, - selected : true, - dragover : true - }, - - __onPointerOver: function() { - this.addState("hovered"); - }, - - __onPointerOut : function() { - this.removeState("hovered"); - }, - - _filter: function() { - this.exclude(); - }, - - _unfilter: function() { - this.show(); - }, - - _shouldApplyFilter: function(data) { - return false; - }, - - _shouldReactToFilter: function(data) { - return false; - } - }, - - destruct: function() { - this.removeListener("pointerover", this.__onPointerOver, this); - this.removeListener("pointerout", this.__onPointerOut, this); - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index 61ac3aae9aec..4c9d53f2b794 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -21,7 +21,7 @@ */ qx.Class.define("osparc.dashboard.FileButtonItem", { - extend: osparc.dashboard.FileButtonBase, + extend: osparc.dashboard.ListButtonBase, /** * @param file {osparc.data.model.File} The file to display @@ -52,42 +52,18 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { init: null, apply: "__applyFile", }, - - title: { - check: "String", - nullable: true, - apply: "__applyTitle", - }, - - modifiedAt: { - check: "Date", - nullable: true, - apply: "__applyModifiedAt", - }, }, members: { _createChildControlImpl: function(id) { let control; switch (id) { - case "icon": { - control = new osparc.dashboard.FolderWithSharedIcon().set({ - anonymous: true, - height: 40, - padding: 5 - }); - this._add(control, osparc.dashboard.FolderButtonBase.POS.ICON); - break; - } - case "title": - control = new qx.ui.basic.Label().set({ - font: "text-14", - }); - this._add(control, osparc.dashboard.FolderButtonBase.POS.TITLE); - break; case "date-by": control = new osparc.ui.basic.DateAndBy(); - this._add(control, osparc.dashboard.FolderButtonBase.POS.SUBTITLE); + this._add(control, { + row: 0, + column: osparc.dashboard.ListButtonBase.POS.LAST_CHANGE + }); break; case "menu-button": { control = new qx.ui.form.MenuButton().set({ @@ -102,8 +78,10 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { control.getContentElement().setStyles({ "border-radius": `${osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS / 2}px` }); - osparc.utils.Utils.setIdToWidget(control, "folderItemMenuButton"); - this._add(control, osparc.dashboard.FolderButtonBase.POS.MENU); + this._add(control, { + row: 0, + column: osparc.dashboard.ListButtonBase.POS.OPTIONS + }); break; } } @@ -112,37 +90,30 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { __applyFile: function(file) { const id = file.getPath(); - this.getChildControl("icon"); this.set({ cardKey: "file-" + id, }); - file.bind("name", this, "title"); - file.bind("modifiedAt", this, "modifiedAt"); - osparc.utils.Utils.setIdToWidget(this, "fileItem_" + id); - this.__addMenuButton(); - }, + const icon = this.getChildControl("icon"); + if (file.getIsDirectory()) { + icon.setSource("@FontAwesome5Solid/folder/24"); + } else { + icon.setSource("@FontAwesome5Solid/file/24"); + } - __applyTitle: function(value) { const label = this.getChildControl("title"); label.set({ - value, - toolTipText: value, + value: file.getName(), + toolTipText: file.getName(), }); - }, - __applyModifiedAt: function(value) { - if (value) { - const dateBy = this.getChildControl("date-by"); - dateBy.set({ - date: value, - toolTipText: this.tr("Last modified"), - }) - } - }, + const dateBy = this.getChildControl("date-by"); + dateBy.set({ + date: file.getModifiedAt(), + toolTipText: this.tr("Last modified"), + }); - __addMenuButton: function() { const menuButton = this.getChildControl("menu-button"); menuButton.setVisibility("visible"); From b9bfb2cc07668ea77465486c79da7b91581e1194 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 16:30:47 +0200 Subject: [PATCH 46/59] openLocation --- .../class/osparc/dashboard/FileButtonItem.js | 6 +++-- .../osparc/dashboard/ResourceBrowserBase.js | 3 ++- .../dashboard/ResourceContainerManager.js | 1 + .../class/osparc/dashboard/StudyBrowser.js | 24 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index 4c9d53f2b794..5e67932d5659 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -123,8 +123,10 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { }); const openLocationButton = new qx.ui.menu.Button(this.tr("Open location"), "@FontAwesome5Solid/folder/12"); - openLocationButton.addListener("execute", () => this.fireDataEvent("openLocation", this.getFolderId()), this); - osparc.utils.Utils.setIdToWidget(openLocationButton, "openLocationMenuItem"); + openLocationButton.addListener("execute", () => this.fireDataEvent("openLocation", { + projectId: file.getProjectId(), + path: file.getPath() + }), this); menu.add(openLocationButton); menuButton.setMenu(menu); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index a8b5967cfeea..e2b2550ad7fb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -357,6 +357,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("trashWorkspaceRequested", e => this._trashWorkspaceRequested(e.getData())); resourcesContainer.addListener("untrashWorkspaceRequested", e => this._untrashWorkspaceRequested(e.getData())); resourcesContainer.addListener("deleteWorkspaceRequested", e => this._deleteWorkspaceRequested(e.getData())); + resourcesContainer.addListener("openLocation", e => this._openLocation(e.getData())); this._addToLayout(resourcesContainer); }, @@ -928,7 +929,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, - _workspaceSelected: function(workspaceId) { + _openLocation: function(data) { throw new Error("Abstract method called!"); }, 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 b0cc96956e7e..665da39545ef 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -103,6 +103,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "changeContext": "qx.event.type.Data", "studyToFolderRequested": "qx.event.type.Data", "folderToFolderRequested": "qx.event.type.Data", + "openLocation": "qx.event.type.Data", }, statics: { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index ce9fdf0d4e99..0a099055492e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -795,6 +795,30 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // It will remove the file cards this._reloadCards(); }, + + _openLocation: function(fileData) { + const projectId = fileData["projectId"]; + const path = fileData["path"]; + this.__openStudyDetails(projectId, path); + }, + + __openStudyDetails: function(projectId, path) { + osparc.store.Study.getInstance().getOne(projectId) + .then(studyData => { + if (studyData) { + const studyDataCopy = osparc.data.model.Study.deepCloneStudyObject(studyData); + studyDataCopy["resourceType"] = "study"; + const { + resourceDetails, + window, + } = osparc.dashboard.ResourceDetails.popUpInWindow(studyDataCopy); + resourceDetails.addListener("openStudy", () => { + const openCB = () => window.close(); + this._startStudyById(projectId, openCB); + }); + } + }); + }, // /FILES __configureStudyCards: function(cards) { From 41ebb4a510e21f46d9ca350cf4a1f7a487736bb9 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 16:39:25 +0200 Subject: [PATCH 47/59] aesthetics --- .../source/class/osparc/dashboard/FileButtonItem.js | 10 ++-------- .../client/source/class/osparc/data/StreamTask.js | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index 5e67932d5659..88727e6b5320 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -30,8 +30,7 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { this.base(arguments); this.set({ - appearance: "pb-study", // change this, - cursor: "auto", + cursor: "default", }); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); @@ -95,12 +94,7 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { }); osparc.utils.Utils.setIdToWidget(this, "fileItem_" + id); - const icon = this.getChildControl("icon"); - if (file.getIsDirectory()) { - icon.setSource("@FontAwesome5Solid/folder/24"); - } else { - icon.setSource("@FontAwesome5Solid/file/24"); - } + this.setIcon(file.getIsDirectory() ? "@FontAwesome5Solid/folder/" : "@FontAwesome5Solid/file/"); const label = this.getChildControl("title"); label.set({ diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index da8e053ed8fb..e94104240bcc 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -51,7 +51,7 @@ qx.Class.define("osparc.data.StreamTask", { check: "Number", nullable: false, // init: 20, - init: 2, + init: 5, }, }, From b89728a7ec540c2937dea5979a74f82c99ccbfab Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 16:47:50 +0200 Subject: [PATCH 48/59] minor --- .../client/source/class/osparc/data/StreamTask.js | 1 - 1 file changed, 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index e94104240bcc..1c7680eb9845 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -59,7 +59,6 @@ qx.Class.define("osparc.data.StreamTask", { // override _startPolling: function() { return; - // this.fetchStream(); }, fetchStream: function() { From b035e64392a3e72a3ac03a9648217a2d3f19b992 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 17:24:10 +0200 Subject: [PATCH 49/59] fitToContainer --- .../dashboard/ResourceContainerManager.js | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) 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 665da39545ef..5dfae544b21b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -44,6 +44,9 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this._add(foldersContainer); const filesContainer = this.__filesContainer = new osparc.dashboard.CardContainer(); + filesContainer.getLayout().set({ + spacingY: osparc.dashboard.ListButtonBase.SPACING, + }); this.__filesContainer.exclude(); this._add(filesContainer); } @@ -134,6 +137,20 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { spacingY: spacing }); }, + + fitToContainer: function(card, container) { + const __fitToContainer = () => { + const bounds = container.getBounds() || container.getSizeHint(); + card.setWidth(bounds.width); + }; + [ + "appear", + "resize", + ].forEach(ev => { + container.addListener(ev, () => __fitToContainer()); + }); + __fitToContainer(); + }, }, members: { @@ -331,17 +348,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { container.add(card); if (this.getMode() === "list") { - const fitToContainer = () => { - const bounds = container.getBounds() || container.getSizeHint(); - card.setWidth(bounds.width); - }; - [ - "appear", - "resize", - ].forEach(ev => { - container.addListener(ev, () => fitToContainer()); - }); - fitToContainer(); + this.self().fitToContainer(card, container); } }, @@ -556,6 +563,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { const card = this.__createFileCard(fileData); this.__filesContainer.add(card); this.__filesContainer.show(); + this.self().fitToContainer(card, this.__filesContainer); return card; }, From 4669e6d6125b000c9c35943d7385173784679905 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Mon, 20 Oct 2025 17:42:46 +0200 Subject: [PATCH 50/59] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 2 +- .../client/source/class/osparc/data/PollTask.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 0a099055492e..52a0d07552d9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -431,10 +431,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; const existingStream = osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text); - // TODO: abort last stream if it changed if (existingStream) { this.__fetchFilesFromStream(existingStream); } else { + // TODO: abort last stream if it changed const streamPromise = osparc.store.Data.getInstance().searchFiles(text); const pollingInterval = 2000; osparc.store.StreamTasks.getInstance().createStreamTask("files_search", text, streamPromise, pollingInterval) diff --git a/services/static-webserver/client/source/class/osparc/data/PollTask.js b/services/static-webserver/client/source/class/osparc/data/PollTask.js index 9d4ee57499da..e00a8da36d5c 100644 --- a/services/static-webserver/client/source/class/osparc/data/PollTask.js +++ b/services/static-webserver/client/source/class/osparc/data/PollTask.js @@ -131,8 +131,6 @@ qx.Class.define("osparc.data.PollTask", { __pollTaskState: function() { const statusPath = this.self().extractPathname(this.getStatusHref()); - // OM remove - return; fetch(statusPath) .then(resp => { if (this.__aborting || this.getDone()) { From d554cee9633c0f7ed11c8e72b7d7c00bf6bb6734 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:02:59 +0200 Subject: [PATCH 51/59] abortStreamTasks --- .../class/osparc/dashboard/StudyBrowser.js | 7 ++++--- .../source/class/osparc/store/StreamTasks.js | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 52a0d07552d9..92f7bebd20fe 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -430,14 +430,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._loadingResourcesBtn.setVisibility("visible"); const filterData = this._searchBarFilter.getFilterData(); const text = filterData.text ? encodeURIComponent(filterData.text) : ""; - const existingStream = osparc.store.StreamTasks.getInstance().getStreamTask("files_search", text); + const streamTasks = osparc.store.StreamTasks.getInstance(); + const existingStream = streamTasks.getStreamTask("files_search", text); if (existingStream) { this.__fetchFilesFromStream(existingStream); } else { - // TODO: abort last stream if it changed + streamTasks.abortStreamTasks(); const streamPromise = osparc.store.Data.getInstance().searchFiles(text); const pollingInterval = 2000; - osparc.store.StreamTasks.getInstance().createStreamTask("files_search", text, streamPromise, pollingInterval) + streamTasks.createStreamTask("files_search", text, streamPromise, pollingInterval) .then(newStream => this.__fetchFilesFromStream(newStream)); } }, diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js index a17b6e7a6aa9..38f2d90d17ee 100644 --- a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -52,10 +52,20 @@ qx.Class.define("osparc.store.StreamTasks", { return tasks[internalId] || null; }, + abortStreamTasks: function() { + const tasks = this.getTasks(); + Object.values(tasks).forEach(stream => { + if (stream.isEnd() === false) { + stream.abortRequested(); + } + // and remove + this.__removeStreamTask(stream); + }); + }, + __addStreamTask: function(action, params, streamData, interval) { const internalId = osparc.store.StreamTasks.actionToInternalId(action, params); const stream = new osparc.data.StreamTask(streamData, interval); - // stream.addListener("resultReceived", () => this.__removeStreamTask(stream), this); stream.addListener("taskAborted", () => this.__removeStreamTask(stream), this); const tasks = this.getTasks(); tasks[internalId] = stream; @@ -64,9 +74,9 @@ qx.Class.define("osparc.store.StreamTasks", { __removeStreamTask: function(stream) { const tasks = this.getTasks(); - const index = tasks.findIndex(t => t.getTaskId() === stream.getTaskId()); - if (index > -1) { - tasks.splice(index, 1); + const internalId = Object.keys(tasks).find(key => tasks[key] === stream); + if (internalId) { + delete tasks[internalId]; } }, } From 4a1321abb893b09a659ba99cb608b4443b85a2a6 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:03:20 +0200 Subject: [PATCH 52/59] aesthetics --- .../class/osparc/dashboard/FileButtonItem.js | 15 +++++++++++---- .../class/osparc/dashboard/ListButtonBase.js | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js index 88727e6b5320..4b117fa7b6e7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FileButtonItem.js @@ -70,6 +70,8 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { padding: [0, 8], maxWidth: osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS, maxHeight: osparc.dashboard.ListButtonItem.MENU_BTN_DIMENSIONS, + alignX: "center", + alignY: "middle", icon: "@FontAwesome5Solid/ellipsis-v/14", focusable: false }); @@ -95,15 +97,20 @@ qx.Class.define("osparc.dashboard.FileButtonItem", { osparc.utils.Utils.setIdToWidget(this, "fileItem_" + id); this.setIcon(file.getIsDirectory() ? "@FontAwesome5Solid/folder/" : "@FontAwesome5Solid/file/"); + this.getChildControl("icon").getChildControl("image").set({ + paddingTop: 5, + }); - const label = this.getChildControl("title"); - label.set({ + this.getChildControl("title").set({ value: file.getName(), toolTipText: file.getName(), }); - const dateBy = this.getChildControl("date-by"); - dateBy.set({ + this.getChildControl("owner").set({ + value: "Project Id: " + osparc.utils.Utils.uuidToShort(file.getProjectId()), + }); + + this.getChildControl("date-by").set({ date: file.getModifiedAt(), toolTipText: this.tr("Last modified"), }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js index 79d650e40885..7d65bb9c9a9a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js @@ -42,7 +42,7 @@ qx.Class.define("osparc.dashboard.ListButtonBase", { }, statics: { - ITEM_HEIGHT: 35, + ITEM_HEIGHT: 40, SPACING: 5, POS: { THUMBNAIL: 0, From cb2c1c5c605a67ce55f1572385b995c03dc92433 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:03:28 +0200 Subject: [PATCH 53/59] remove not found text --- .../source/class/osparc/dashboard/ResourceContainerManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5dfae544b21b..58cd82839993 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -192,7 +192,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { text = this.tr("No Functions found"); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: - text = this.tr("No Files found"); + // text = this.tr("No Files found"); break; } break; From 42f4eb6decf8e7b988d67431d77f38ef3b40ee54 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:22:57 +0200 Subject: [PATCH 54/59] No Files found --- .../dashboard/ResourceContainerManager.js | 56 ++++++++++++------- .../class/osparc/dashboard/StudyBrowser.js | 14 +++-- 2 files changed, 46 insertions(+), 24 deletions(-) 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 58cd82839993..e9f2aa58f7aa 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -51,12 +51,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this._add(filesContainer); } - const noResourcesFound = this.__noResourcesFound = new qx.ui.basic.Label("No resources found").set({ - visibility: "excluded", - font: "text-14" - }); - noResourcesFound.exclude(); - this._add(noResourcesFound); + this.getChildControl("no-resources-found"); const nonGroupedContainer = this.__nonGroupedContainer = this.__createFlatList(); this._add(nonGroupedContainer); @@ -165,10 +160,24 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { __nonGroupedContainer: null, __groupedContainers: null, __resourceType: null, - __noResourcesFound: null, __noResourcesFoundTimer: null, - __evaluateNoResourcesFoundLabel: function() { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "no-resources-found": + control = new qx.ui.basic.Label().set({ + value: this.tr("No Resources found"), + visibility: "excluded", + font: "text-14", + }); + this._add(control); + break; + } + return control || this.base(arguments, id); + }, + + __getNotFoundText: function() { let text = null; switch (this.__resourceType) { case "study": { @@ -191,9 +200,6 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FUNCTIONS: text = this.tr("No Functions found"); break; - case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_FILES: - // text = this.tr("No Files found"); - break; } break; } @@ -203,26 +209,36 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { case "service": text = this.tr("No Apps found"); break; - default: - text = this.tr("No Resources found"); - break; } + return text; + }, - this.__noResourcesFound.exclude(); + __evaluateNoResourcesFoundLabel: function() { + const noResourcesFound = this.getChildControl("no-resources-found"); + noResourcesFound.exclude(); if (this.__noResourcesFoundTimer) { clearTimeout(this.__noResourcesFoundTimer); } - if (text && this.__resourcesList.length === 0) { + + if (this.__resourcesList.length === 0) { // delay it a bit to avoid the initial flickering this.__noResourcesFoundTimer = setTimeout(() => { - this.__noResourcesFound.set({ - value: text, - visibility: "visible", - }); + this.showNoResourcesFound(); }, 2000); } }, + showNoResourcesFound: function() { + const text = this.__getNotFoundText(); + if (text) { + const noResourcesFound = this.getChildControl("no-resources-found"); + noResourcesFound.set({ + value: text, + }); + noResourcesFound.show(); + } + }, + addNonResourceCard: function(card) { if (osparc.dashboard.CardContainer.isValidCard(card)) { let groupContainer = null; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 92f7bebd20fe..cd03aeac3cdb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -447,14 +447,20 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { stream.fetchStream() .then(streamData => { const items = streamData["data"]["items"] || []; + const end = streamData["data"]["end"] || false; + stream.setEnd(end); if (items.length) { this.__addFilesToList(items); } - const end = streamData["data"]["end"] || false; - stream.setEnd(end); - - // a bit hacky + // keep it not null to allow further fetching this._resourcesContainer.getFlatList().nextRequest = !end; + // show no files found only if the stream ended and no files were found + if (end && this.__filesList.length === 0) { + this._resourcesContainer.getChildControl("no-resources-found").set({ + value: this.tr("No Files found"), + visibility: "visible", + }); + } }) .catch(err => { osparc.FlashMessenger.logError(err); From db636934b16ff1fdca55121935f9acc0b2b7f5d6 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:25:37 +0200 Subject: [PATCH 55/59] aesthetics --- .../source/class/osparc/dashboard/ListButtonLoadMore.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js index 1f0fad3e4a6b..c7be9976f644 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js @@ -39,12 +39,12 @@ qx.Class.define("osparc.dashboard.ListButtonLoadMore", { members: { _applyFetching: function(value) { this.setIcon(osparc.dashboard.CardBase.LOADING_ICON); + const image = this.getChildControl("icon").getChildControl("image"); + image.setPadding(5); // add padding to make the rotation smoother if (value) { - this.getChildControl("icon").getChildControl("image").getContentElement() - .addClass("rotate"); + image.getContentElement().addClass("rotate"); } else { - this.getChildControl("icon").getChildControl("image").getContentElement() - .removeClass("rotate"); + image.getContentElement().removeClass("rotate"); } this.setEnabled(!value); }, From 0cab0c3c24b9ad30e248cd44a46182d08a37a1b7 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:26:00 +0200 Subject: [PATCH 56/59] minor --- .../client/source/class/osparc/store/StreamTasks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js index 38f2d90d17ee..1152a90c33db 100644 --- a/services/static-webserver/client/source/class/osparc/store/StreamTasks.js +++ b/services/static-webserver/client/source/class/osparc/store/StreamTasks.js @@ -37,7 +37,6 @@ qx.Class.define("osparc.store.StreamTasks", { createStreamTask: function(action, params, streamPromise, interval) { return streamPromise .then(streamData => { - console.log("Stream data received:", streamData); if (!("stream_href" in streamData)) { throw new Error("Stream href missing"); } From f37d7274d06742530da186f9b7cd44d201be54bd Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 09:31:32 +0200 Subject: [PATCH 57/59] minors --- .../source/class/osparc/dashboard/SearchBarFilterExtended.js | 3 ++- .../client/source/class/osparc/data/StreamTask.js | 1 - .../static-webserver/client/source/class/osparc/store/Data.js | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index e1a36defc311..93d7f91c881b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -186,7 +186,8 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { case "date-filters": control = new osparc.desktop.credits.DateFilters(); control.addListener("change", e => { - console.log(e.getData()); + const dateRange = e.getData(); + this.__filter("modifiedAt", dateRange); }); this.getChildControl("filters-layout").add(control); break; diff --git a/services/static-webserver/client/source/class/osparc/data/StreamTask.js b/services/static-webserver/client/source/class/osparc/data/StreamTask.js index 1c7680eb9845..39ddc46f512c 100644 --- a/services/static-webserver/client/source/class/osparc/data/StreamTask.js +++ b/services/static-webserver/client/source/class/osparc/data/StreamTask.js @@ -50,7 +50,6 @@ qx.Class.define("osparc.data.StreamTask", { pageSize: { check: "Number", nullable: false, - // init: 20, init: 5, }, }, diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index 67f6e738210b..09971d1181b6 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -261,8 +261,7 @@ qx.Class.define("osparc.store.Data", { } } }; - if (modifiedAtFrom && modifiedAtFrom) { - // OM todo + if (modifiedAtFrom && modifiedAtTo) { params.data["filters"]["modifiedAtFrom"] = modifiedAtFrom; params.data["filters"]["modifiedAtTo"] = modifiedAtTo; } From 49b7bcfeeb2f280d9790452d1108953ea20ca91f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 10:10:05 +0200 Subject: [PATCH 58/59] osparc.filter.DateFilters --- .../dashboard/SearchBarFilterExtended.js | 4 +- .../source/class/osparc/filter/DateFilters.js | 202 ++++++++++++++++++ 2 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/filter/DateFilters.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index 93d7f91c881b..5b2023cc6566 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -184,7 +184,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { this.getChildControl("filter-buttons").add(control); break; case "date-filters": - control = new osparc.desktop.credits.DateFilters(); + control = new osparc.filter.DateFilters(); control.addListener("change", e => { const dateRange = e.getData(); this.__filter("modifiedAt", dateRange); @@ -322,7 +322,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Files")); sharedWithButton.exclude(); tagsButton.exclude(); - // dateFilters.show(); + dateFilters.show(); break; } }, diff --git a/services/static-webserver/client/source/class/osparc/filter/DateFilters.js b/services/static-webserver/client/source/class/osparc/filter/DateFilters.js new file mode 100644 index 000000000000..f0307e745f19 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/filter/DateFilters.js @@ -0,0 +1,202 @@ +/* + * oSPARC - The SIMCORE frontend - https://osparc.io + * Copyright: 2023 IT'IS Foundation - https://itis.swiss + * License: MIT - https://opensource.org/licenses/MIT + * Authors: Ignacio Pascual (ignapas) + * Odei Maiz (odeimaiz) + */ + +qx.Class.define("osparc.filter.DateFilters", { + extend: qx.ui.core.Widget, + + construct() { + this.base(arguments); + this._setLayout(new qx.ui.layout.HBox(6)); + + this._add(this.getChildControl("range")); + this._add(this.getChildControl("from")); + this._add(this.getChildControl("until")); + + // initialize to default (Last 7 days) + const selectbox = this.getChildControl("range").getUserData("selectbox"); + selectbox.setSelection([selectbox.getSelectables()[1]]); // "last_7_days" + }, + + events: { + "change": "qx.event.type.Data", + }, + + statics: { + RANGES: { + TODAY: { + id: "today", + label: "Today", + }, + LAST_7_DAYS: { + id: "last_7_days", + label: "Last 7 days", + }, + LAST_30_DAYS: { + id: "last_30_days", + label: "Last 30 days", + }, + LAST_YEAR: { + id: "last_year", + label: "Last year", + }, + CUSTOM: { + id: "custom", + label: "Custom", + }, + }, + }, + + members: { + _createChildControlImpl(id, hash) { + let control; + switch (id) { + case "range": { + const container = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); + container.add(new qx.ui.basic.Label(this.tr("Time Range"))); + const select = new qx.ui.form.SelectBox().set({ allowStretchY: false }); + Object.values(this.self().RANGES).forEach(({ id, label }) => { + const item = new qx.ui.form.ListItem(label); + item.setModel(id); + select.add(item); + }); + select.addListener("changeSelection", () => { + const model = select.getSelection()[0].getModel(); + this.__applyFilter(model); + }); + container.add(select, { flex: 1 }); + control = container; + control.setUserData("selectbox", select); + break; + } + case "from": { + const c = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); + c.add(new qx.ui.basic.Label("From")); + c.add(this.getChildControl("from-datefield")); + control = c; + break; + } + case "until": { + const c = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); + c.add(new qx.ui.basic.Label("Until")); + c.add(this.getChildControl("until-datefield")); + control = c; + break; + } + case "from-datefield": { + const df = new qx.ui.form.DateField(); + df.setValue(new Date()); + df.addListener("changeValue", e => this.__onDateChange(e)); + control = df; + break; + } + case "until-datefield": { + const df = new qx.ui.form.DateField(); + df.setValue(new Date()); + df.addListener("changeValue", e => this.__onDateChange(e)); + control = df; + break; + } + } + + return control || this.base(arguments, id, hash); + }, + + __applyFilter(filterId) { + const fromContainer = this.getChildControl("from"); + const untilContainer = this.getChildControl("until"); + const fromField = this.getChildControl("from-datefield"); + const untilField = this.getChildControl("until-datefield"); + + const isCustom = filterId === this.self().RANGES.CUSTOM.id; + fromContainer.setVisibility(isCustom ? "visible" : "excluded"); + untilContainer.setVisibility(isCustom ? "visible" : "excluded"); + + if (!isCustom) { + const { from, until } = this.__computeRange(filterId); + fromField.setValue(from); + untilField.setValue(until); + } + + this.fireDataEvent("change", this.getValue()); + }, + + __computeRange(filterId) { + const today = this.__startOfDay(new Date()); + let from = new Date(today); + let until = new Date(today); + + switch (filterId) { + case this.self().RANGES.TODAY.id: + break; + + case this.self().RANGES.LAST_7_DAYS.id: + from.setDate(today.getDate() - 7); + break; + + case this.self().RANGES.LAST_30_DAYS.id: + from.setDate(today.getDate() - 30); + break; + + case this.self().RANGES.LAST_YEAR.id: + from.setFullYear(today.getFullYear() - 1); + break; + + case this.self().RANGES.CUSTOM.id: + default: + from = this.getChildControl("from-datefield").getValue() || from; + until = this.getChildControl("until-datefield").getValue() || until; + break; + } + + return { from, until }; + }, + + __startOfDay(d) { + const nd = new Date(d); + nd.setHours(0, 0, 0, 0); + return nd; + }, + + __onDateChange(e) { + const select = this.getChildControl("range").getUserData("selectbox"); + const filterId = select.getSelection()[0].getModel(); + const isCustom = filterId === this.self().RANGES.CUSTOM.id; + + if (!isCustom) return; + + const fromField = this.getChildControl("from-datefield"); + const untilField = this.getChildControl("until-datefield"); + const fromDate = fromField.getValue(); + const untilDate = untilField.getValue(); + + if (!fromDate || !untilDate) return; + + if (fromDate.getTime() > untilDate.getTime()) { + if (e.getCurrentTarget() === fromField) { + untilField.setValue(new Date(fromDate.getTime())); + } else { + fromField.setValue(new Date(untilDate.getTime())); + } + } + + this.fireDataEvent("change", this.getValue()); + }, + + getValue() { + const selectbox = this.getChildControl("range").getUserData("selectbox"); + const filterId = selectbox.getSelection()[0].getModel(); + const { from, until } = this.__computeRange(filterId); + + return { + filter: filterId, + from: osparc.utils.Utils.formatDateYyyyMmDd(from), + until: osparc.utils.Utils.formatDateYyyyMmDd(until) + }; + }, + }, +}); From 0737ac40c4ee7a37db90a9da1709b81c026abfc2 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Tue, 21 Oct 2025 10:20:24 +0200 Subject: [PATCH 59/59] move file --- .../dashboard/SearchBarFilterExtended.js | 2 +- .../osparc/desktop/credits/DateFilters.js | 121 -------- .../desktop/credits/ResourceInTableViewer.js | 2 +- .../osparc/desktop/credits/Transactions.js | 2 +- .../source/class/osparc/filter/DateFilters.js | 261 ++++++------------ 5 files changed, 93 insertions(+), 295 deletions(-) delete mode 100644 services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js index 5b2023cc6566..8d99efe602a4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilterExtended.js @@ -322,7 +322,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilterExtended", { searchBarFilter.getChildControl("text-field").setPlaceholder(this.tr("Search in Files")); sharedWithButton.exclude(); tagsButton.exclude(); - dateFilters.show(); + dateFilters.exclude(); break; } }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js b/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js deleted file mode 100644 index 637dac813b3e..000000000000 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/DateFilters.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * oSPARC - The SIMCORE frontend - https://osparc.io - * Copyright: 2023 IT'IS Foundation - https://itis.swiss - * License: MIT - https://opensource.org/licenses/MIT - * Authors: Ignacio Pascual (ignapas) - */ - -qx.Class.define("osparc.desktop.credits.DateFilters", { - extend: qx.ui.core.Widget, - - construct() { - this.base(arguments); - this._setLayout(new qx.ui.layout.HBox(6)); - this.__buildLayout(); - }, - - events: { - "change": "qx.event.type.Data" - }, - - members: { - __buildLayout() { - this._removeAll(); - - // Range defaults: today - const defaultFrom = new Date(); - const defaultTo = new Date(); - - this.__from = this.__addDateInput("From", defaultFrom); - this.__until = this.__addDateInput("Until", defaultTo); - - const todayBtn = new qx.ui.form.Button("Today").set({ - allowStretchY: false, - alignY: "bottom" - }); - todayBtn.addListener("execute", () => { - const today = new Date(); - this.__from.setValue(today); - this.__until.setValue(today); - }); - this._add(todayBtn); - - const lastWeekBtn = new qx.ui.form.Button("Last week").set({ - allowStretchY: false, - alignY: "bottom" - }); - lastWeekBtn.addListener("execute", () => { - const today = new Date(); - const lastWeek = new Date(today); - lastWeek.setDate(today.getDate() - 7) - this.__from.setValue(lastWeek); - this.__until.setValue(today); - }); - this._add(lastWeekBtn); - - const lastMonthBtn = new qx.ui.form.Button("Last month").set({ - allowStretchY: false, - alignY: "bottom" - }); - lastMonthBtn.addListener("execute", () => { - const today = new Date(); - const lastMonth = new Date(today); - lastMonth.setMonth(today.getMonth() - 1); - this.__from.setValue(lastMonth); - this.__until.setValue(today); - }); - this._add(lastMonthBtn); - - const lastYearBtn = new qx.ui.form.Button("Last year").set({ - allowStretchY: false, - alignY: "bottom" - }); - lastYearBtn.addListener("execute", () => { - const today = new Date(); - const lastYear = new Date(today); - lastYear.setYear(today.getFullYear() - 1); - this.__from.setValue(lastYear); - this.__until.setValue(today); - }); - this._add(lastYearBtn); - }, - - __addDateInput(label, initDate) { - const container = new qx.ui.container.Composite(new qx.ui.layout.VBox()); - const lbl = new qx.ui.basic.Label(label); - container.add(lbl); - const datepicker = new qx.ui.form.DateField(); - datepicker.setValue(initDate ? initDate : new Date()); - datepicker.addListener("changeValue", e => this.__changeHandler(e)); - container.add(datepicker); - this._add(container); - return datepicker; - }, - - __changeHandler: function(e) { - const timestampFrom = this.__from.getValue().getTime(); - const timestampUntil = this.__until.getValue().getTime(); - if (timestampFrom > timestampUntil) { - // 'From' date must be before 'until' - if (e.getCurrentTarget() === this.__from) { - // Adapt the date the user did not change - this.__until.setValue(new Date(this.__from.getValue().getTime())); - } else { - this.__from.setValue(new Date(this.__until.getValue().getTime())); - } - return; - } - const value = this.getValue(); - this.fireDataEvent("change", value); - }, - - getValue: function() { - const from = osparc.utils.Utils.formatDateYyyyMmDd(this.__from.getValue()); - const until = osparc.utils.Utils.formatDateYyyyMmDd(this.__until.getValue()); - return { - from, - until - }; - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/ResourceInTableViewer.js b/services/static-webserver/client/source/class/osparc/desktop/credits/ResourceInTableViewer.js index 7c44a204b747..7b75887b8462 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/ResourceInTableViewer.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/ResourceInTableViewer.js @@ -66,7 +66,7 @@ qx.Class.define("osparc.desktop.credits.ResourceInTableViewer", { this._add(control); break; case "date-filters": - control = new osparc.desktop.credits.DateFilters(); + control = new osparc.filter.DateFilters(); control.addListener("change", e => { const table = this.getChildControl("table"); table.getTableModel().setFilters(e.getData()); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/Transactions.js b/services/static-webserver/client/source/class/osparc/desktop/credits/Transactions.js index 610ab82754e6..408ff327e14e 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/Transactions.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/Transactions.js @@ -58,7 +58,7 @@ qx.Class.define("osparc.desktop.credits.Transactions", { // FEATURE TOGGLE // const filterContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox()) - // this.__dateFilters = new osparc.desktop.credits.DateFilters(); + // this.__dateFilters = new osparc.filter.DateFilters(); // this.__dateFilters.addListener("change", e => this.__saveFilters(e.getData())); // filterContainer.add(this.__dateFilters); diff --git a/services/static-webserver/client/source/class/osparc/filter/DateFilters.js b/services/static-webserver/client/source/class/osparc/filter/DateFilters.js index f0307e745f19..93073bbc4533 100644 --- a/services/static-webserver/client/source/class/osparc/filter/DateFilters.js +++ b/services/static-webserver/client/source/class/osparc/filter/DateFilters.js @@ -3,7 +3,6 @@ * Copyright: 2023 IT'IS Foundation - https://itis.swiss * License: MIT - https://opensource.org/licenses/MIT * Authors: Ignacio Pascual (ignapas) - * Odei Maiz (odeimaiz) */ qx.Class.define("osparc.filter.DateFilters", { @@ -12,191 +11,111 @@ qx.Class.define("osparc.filter.DateFilters", { construct() { this.base(arguments); this._setLayout(new qx.ui.layout.HBox(6)); - - this._add(this.getChildControl("range")); - this._add(this.getChildControl("from")); - this._add(this.getChildControl("until")); - - // initialize to default (Last 7 days) - const selectbox = this.getChildControl("range").getUserData("selectbox"); - selectbox.setSelection([selectbox.getSelectables()[1]]); // "last_7_days" + this.__buildLayout(); }, events: { - "change": "qx.event.type.Data", - }, - - statics: { - RANGES: { - TODAY: { - id: "today", - label: "Today", - }, - LAST_7_DAYS: { - id: "last_7_days", - label: "Last 7 days", - }, - LAST_30_DAYS: { - id: "last_30_days", - label: "Last 30 days", - }, - LAST_YEAR: { - id: "last_year", - label: "Last year", - }, - CUSTOM: { - id: "custom", - label: "Custom", - }, - }, + "change": "qx.event.type.Data" }, members: { - _createChildControlImpl(id, hash) { - let control; - switch (id) { - case "range": { - const container = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); - container.add(new qx.ui.basic.Label(this.tr("Time Range"))); - const select = new qx.ui.form.SelectBox().set({ allowStretchY: false }); - Object.values(this.self().RANGES).forEach(({ id, label }) => { - const item = new qx.ui.form.ListItem(label); - item.setModel(id); - select.add(item); - }); - select.addListener("changeSelection", () => { - const model = select.getSelection()[0].getModel(); - this.__applyFilter(model); - }); - container.add(select, { flex: 1 }); - control = container; - control.setUserData("selectbox", select); - break; - } - case "from": { - const c = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); - c.add(new qx.ui.basic.Label("From")); - c.add(this.getChildControl("from-datefield")); - control = c; - break; - } - case "until": { - const c = new qx.ui.container.Composite(new qx.ui.layout.HBox(2)); - c.add(new qx.ui.basic.Label("Until")); - c.add(this.getChildControl("until-datefield")); - control = c; - break; - } - case "from-datefield": { - const df = new qx.ui.form.DateField(); - df.setValue(new Date()); - df.addListener("changeValue", e => this.__onDateChange(e)); - control = df; - break; - } - case "until-datefield": { - const df = new qx.ui.form.DateField(); - df.setValue(new Date()); - df.addListener("changeValue", e => this.__onDateChange(e)); - control = df; - break; - } - } - - return control || this.base(arguments, id, hash); + __buildLayout() { + this._removeAll(); + + // Range defaults: today + const defaultFrom = new Date(); + const defaultTo = new Date(); + + this.__from = this.__addDateInput("From", defaultFrom); + this.__until = this.__addDateInput("Until", defaultTo); + + const todayBtn = new qx.ui.form.Button("Today").set({ + allowStretchY: false, + alignY: "bottom" + }); + todayBtn.addListener("execute", () => { + const today = new Date(); + this.__from.setValue(today); + this.__until.setValue(today); + }); + this._add(todayBtn); + + const lastWeekBtn = new qx.ui.form.Button("Last week").set({ + allowStretchY: false, + alignY: "bottom" + }); + lastWeekBtn.addListener("execute", () => { + const today = new Date(); + const lastWeek = new Date(today); + lastWeek.setDate(today.getDate() - 7) + this.__from.setValue(lastWeek); + this.__until.setValue(today); + }); + this._add(lastWeekBtn); + + const lastMonthBtn = new qx.ui.form.Button("Last month").set({ + allowStretchY: false, + alignY: "bottom" + }); + lastMonthBtn.addListener("execute", () => { + const today = new Date(); + const lastMonth = new Date(today); + lastMonth.setMonth(today.getMonth() - 1); + this.__from.setValue(lastMonth); + this.__until.setValue(today); + }); + this._add(lastMonthBtn); + + const lastYearBtn = new qx.ui.form.Button("Last year").set({ + allowStretchY: false, + alignY: "bottom" + }); + lastYearBtn.addListener("execute", () => { + const today = new Date(); + const lastYear = new Date(today); + lastYear.setYear(today.getFullYear() - 1); + this.__from.setValue(lastYear); + this.__until.setValue(today); + }); + this._add(lastYearBtn); }, - __applyFilter(filterId) { - const fromContainer = this.getChildControl("from"); - const untilContainer = this.getChildControl("until"); - const fromField = this.getChildControl("from-datefield"); - const untilField = this.getChildControl("until-datefield"); - - const isCustom = filterId === this.self().RANGES.CUSTOM.id; - fromContainer.setVisibility(isCustom ? "visible" : "excluded"); - untilContainer.setVisibility(isCustom ? "visible" : "excluded"); - - if (!isCustom) { - const { from, until } = this.__computeRange(filterId); - fromField.setValue(from); - untilField.setValue(until); - } - - this.fireDataEvent("change", this.getValue()); + __addDateInput(label, initDate) { + const container = new qx.ui.container.Composite(new qx.ui.layout.VBox()); + const lbl = new qx.ui.basic.Label(label); + container.add(lbl); + const datepicker = new qx.ui.form.DateField(); + datepicker.setValue(initDate ? initDate : new Date()); + datepicker.addListener("changeValue", e => this.__changeHandler(e)); + container.add(datepicker); + this._add(container); + return datepicker; }, - __computeRange(filterId) { - const today = this.__startOfDay(new Date()); - let from = new Date(today); - let until = new Date(today); - - switch (filterId) { - case this.self().RANGES.TODAY.id: - break; - - case this.self().RANGES.LAST_7_DAYS.id: - from.setDate(today.getDate() - 7); - break; - - case this.self().RANGES.LAST_30_DAYS.id: - from.setDate(today.getDate() - 30); - break; - - case this.self().RANGES.LAST_YEAR.id: - from.setFullYear(today.getFullYear() - 1); - break; - - case this.self().RANGES.CUSTOM.id: - default: - from = this.getChildControl("from-datefield").getValue() || from; - until = this.getChildControl("until-datefield").getValue() || until; - break; - } - - return { from, until }; - }, - - __startOfDay(d) { - const nd = new Date(d); - nd.setHours(0, 0, 0, 0); - return nd; - }, - - __onDateChange(e) { - const select = this.getChildControl("range").getUserData("selectbox"); - const filterId = select.getSelection()[0].getModel(); - const isCustom = filterId === this.self().RANGES.CUSTOM.id; - - if (!isCustom) return; - - const fromField = this.getChildControl("from-datefield"); - const untilField = this.getChildControl("until-datefield"); - const fromDate = fromField.getValue(); - const untilDate = untilField.getValue(); - - if (!fromDate || !untilDate) return; - - if (fromDate.getTime() > untilDate.getTime()) { - if (e.getCurrentTarget() === fromField) { - untilField.setValue(new Date(fromDate.getTime())); + __changeHandler: function(e) { + const timestampFrom = this.__from.getValue().getTime(); + const timestampUntil = this.__until.getValue().getTime(); + if (timestampFrom > timestampUntil) { + // 'From' date must be before 'until' + if (e.getCurrentTarget() === this.__from) { + // Adapt the date the user did not change + this.__until.setValue(new Date(this.__from.getValue().getTime())); } else { - fromField.setValue(new Date(untilDate.getTime())); + this.__from.setValue(new Date(this.__until.getValue().getTime())); } + return; } - - this.fireDataEvent("change", this.getValue()); + const value = this.getValue(); + this.fireDataEvent("change", value); }, - getValue() { - const selectbox = this.getChildControl("range").getUserData("selectbox"); - const filterId = selectbox.getSelection()[0].getModel(); - const { from, until } = this.__computeRange(filterId); - + getValue: function() { + const from = osparc.utils.Utils.formatDateYyyyMmDd(this.__from.getValue()); + const until = osparc.utils.Utils.formatDateYyyyMmDd(this.__until.getValue()); return { - filter: filterId, - from: osparc.utils.Utils.formatDateYyyyMmDd(from), - until: osparc.utils.Utils.formatDateYyyyMmDd(until) + from, + until }; - }, - }, + } + } });