diff --git a/services/static-webserver/client/source/class/osparc/DownloadLinkTracker.js b/services/static-webserver/client/source/class/osparc/DownloadLinkTracker.js index 27128e42ad0..af259e97a8c 100644 --- a/services/static-webserver/client/source/class/osparc/DownloadLinkTracker.js +++ b/services/static-webserver/client/source/class/osparc/DownloadLinkTracker.js @@ -33,9 +33,10 @@ qx.Class.define("osparc.DownloadLinkTracker", { downloadAnchorNode.setAttribute("href", url); downloadAnchorNode.setAttribute("download", fileName); downloadAnchorNode.setAttribute("osparc", "downloadFile"); + document.body.appendChild(downloadAnchorNode); this.setDownloading(true); downloadAnchorNode.click(); - downloadAnchorNode.remove(); + document.body.removeChild(downloadAnchorNode); // This is needed to make it work in Firefox setTimeout(() => this.setDownloading(false), 100); } diff --git a/services/static-webserver/client/source/class/osparc/admin/AdminCenter.js b/services/static-webserver/client/source/class/osparc/admin/AdminCenter.js index 36329e85b97..bd821027575 100644 --- a/services/static-webserver/client/source/class/osparc/admin/AdminCenter.js +++ b/services/static-webserver/client/source/class/osparc/admin/AdminCenter.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.admin.AdminCenter", { const miniProfile = osparc.desktop.account.MyAccount.createMiniProfileView().set({ paddingRight: 10 }); - this.addWidgetOnTopOfTheTabs(miniProfile); + this.addWidgetToTabs(miniProfile); this.__addPricingPlansPage(); this.__addMaintenancePage(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 25db4f49019..610f40e5e23 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -963,8 +963,8 @@ qx.Class.define("osparc.dashboard.CardBase", { }, openData: function() { - const moreOpts = this.__openMoreOptions(); - moreOpts.openData(); + const resourceData = this.getResourceData(); + osparc.widget.StudyDataManager.popUpInWindow(resourceData["uuid"]); }, openBilling: function() { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 1b629b88585..259782e213f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -93,7 +93,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { __resourceData: null, __resourceModel: null, __infoPage: null, - __dataPage: null, __servicesUpdatePage: null, __permissionsPage: null, __tagsPage: null, @@ -216,10 +215,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this._openPage(this.__infoPage); }, - openData: function() { - this._openPage(this.__dataPage); - }, - openUpdateServices: function() { this._openPage(this.__servicesUpdatePage); }, @@ -306,7 +301,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.__getBillingPage, this.__getServicesUpdatePage, this.__getServicesBootOptionsPage, - this.__getDataPage, this.__getCommentsPage, this.__getPermissionsPage, this.__getSaveAsTemplatePage, @@ -323,6 +317,28 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { } }); + const resourceData = this.__resourceData; + if (!osparc.utils.Resources.isService(resourceData)) { + const title = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + this.tr(" Files..."); + const iconSrc = "@FontAwesome5Solid/file/22"; + const dataAccess = new qx.ui.basic.Atom().set({ + label: title, + icon: iconSrc, + font: "text-14", + padding: 8, + paddingLeft: 12, + gap: 14, + cursor: "pointer", + }); + dataAccess.addListener("tap", () => osparc.widget.StudyDataManager.popUpInWindow(resourceData["uuid"])); + this.addWidgetToTabs(dataAccess); + + if (resourceData["resourceType"] === "study") { + const canShowData = osparc.study.Utils.canShowStudyData(resourceData); + dataAccess.setEnabled(canShowData); + } + } + if (selectedTabId) { const pageFound = tabsView.getChildren().find(page => page.tabId === selectedTabId); if (pageFound) { @@ -486,33 +502,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { return page; }, - __getDataPage: function() { - const resourceData = this.__resourceData; - if (osparc.utils.Resources.isService(resourceData)) { - return null; - } - - const id = "Data"; - const title = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + this.tr(" Files"); - const iconSrc = "@FontAwesome5Solid/file/22"; - const page = this.__dataPage = new osparc.dashboard.resources.pages.BasePage(title, iconSrc, id); - this.__addOpenButton(page); - - if (this.__resourceData["resourceType"] === "study") { - const studyData = this.__resourceData; - const canBeOpened = osparc.study.Utils.canShowStudyData(studyData); - page.setEnabled(canBeOpened); - } - - const lazyLoadContent = () => { - const studyDataManager = new osparc.widget.NodeDataManager(resourceData["uuid"]); - page.addToContent(studyDataManager); - } - page.addListenerOnce("appear", lazyLoadContent, this); - - return page; - }, - __getPermissionsPage: function() { const id = "Permissions"; const title = this.tr("Sharing"); diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js index 9d7f33e55e3..26fa16ddff6 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js +++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js @@ -722,7 +722,7 @@ qx.Class.define("osparc.desktop.StudyEditor", { this.__workbenchView.showPipeline(); } else { const currentNodeId = this.getStudy().getUi().getCurrentNodeId(); - if (currentNodeId) { + if (currentNodeId && this.getStudy().getWorkbench().getNode(currentNodeId)) { const node = this.getStudy().getWorkbench().getNode(currentNodeId); if (node && node.isDynamic()) { this.__workbenchView.fullscreenNode(currentNodeId); diff --git a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js index f1999ebb99f..6763a66b7c6 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js @@ -54,11 +54,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { }); }, - openNodeDataManager: function(node) { - const nodeDataManager = new osparc.widget.NodeDataManager(null, node.getNodeId()); - const win = osparc.ui.window.Window.popUpInWindow(nodeDataManager, node.getLabel(), 900, 600).set({ - appearance: "service-window" - }); + openStudyDataManager: function(node) { + const win = osparc.widget.StudyDataManager.popUpInWindow(null, node.getNodeId(), node.getLabel()); const closeBtn = win.getChildControl("close-button"); osparc.utils.Utils.setIdToWidget(closeBtn, "nodeDataManagerCloseBtn"); } @@ -1049,7 +1046,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { allowGrowY: false }); osparc.utils.Utils.setIdToWidget(nodeFilesBtn, "nodeFilesBtn"); - nodeFilesBtn.addListener("execute", () => this.self().openNodeDataManager(node)); + nodeFilesBtn.addListener("execute", () => this.self().openStudyDataManager(node)); outputsBox.add(nodeFilesBtn); const outputs = new osparc.desktop.PanelView(this.tr("Outputs"), outputsBox); diff --git a/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js b/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js index 0df1dc1d8be..05d2c4d2bb0 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js +++ b/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.desktop.account.MyAccount", { const miniProfile = osparc.desktop.account.MyAccount.createMiniProfileView().set({ paddingRight: 10 }); - this.addWidgetOnTopOfTheTabs(miniProfile); + this.addWidgetToTabs(miniProfile); this.__profilePage = this.__addProfilePage(); diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js b/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js index d27acc99fe0..5eaf26b6947 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", { const miniWallet = this.self().createMiniWalletView().set({ paddingRight: 10 }); - this.addWidgetOnTopOfTheTabs(miniWallet); + this.addWidgetToTabs(miniWallet); this.__walletsPage = this.__addWalletsPage(); this.__paymentMethodsPage = this.__addPaymentMethodsPage(); diff --git a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js index 43113f880e2..2e270c26709 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js +++ b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js @@ -108,23 +108,11 @@ qx.Class.define("osparc.file.FileLabelWithActions", { setItemSelected: function(selectedItem) { if (selectedItem) { + this.__selection = [selectedItem]; const isFile = osparc.file.FilesTree.isFile(selectedItem); this.getChildControl("download-button").setEnabled(isFile); - this.getChildControl("delete-button").setEnabled(isFile); - const selectedLabel = this.getChildControl("selected-label"); - if (isFile) { - this.__selection = [selectedItem]; - selectedLabel.set({ - value: selectedItem.getLabel(), - toolTipText: selectedItem.getFileId() - }); - } else { - this.__selection = []; - selectedLabel.set({ - value: "", - toolTipText: "" - }); - } + this.getChildControl("delete-button").setEnabled(true); // folders can also be deleted + this.getChildControl("selected-label").setValue(selectedItem.getLabel()); } else { this.resetSelection(); } @@ -138,7 +126,7 @@ qx.Class.define("osparc.file.FileLabelWithActions", { } else { const selectedLabel = this.getChildControl("selected-label"); selectedLabel.set({ - value: multiSelectionData.length + " files" + value: multiSelectionData.length + " items" }); } } else { @@ -168,60 +156,86 @@ qx.Class.define("osparc.file.FileLabelWithActions", { } }, + __retrieveURLAndDownloadFile: function(file) { + const fileId = file.getFileId(); + const locationId = file.getLocation(); + osparc.utils.Utils.retrieveURLAndDownload(locationId, fileId) + .then(data => { + if (data) { + osparc.DownloadLinkTracker.getInstance().downloadLinkUnattended(data.link, data.fileName); + } + }); + }, + __deleteSelected: function() { + const toBeDeleted = []; + let isFolderSelected = false; if (this.isMultiSelect()) { - const requests = []; this.__selection.forEach(selection => { - if (selection && osparc.file.FilesTree.isFile(selection)) { - const request = this.__deleteFile(selection); - if (request) { - requests.push(request); + if (selection) { + toBeDeleted.push(selection); + if (osparc.file.FilesTree.isDir(selection)) { + isFolderSelected = true; } } }); - Promise.all(requests) - .then(datas => { - if (datas.length) { - this.fireDataEvent("fileDeleted", datas[0]); - osparc.FlashMessenger.getInstance().logAs(this.tr("Files successfully deleted"), "INFO"); - } - }); - requests } else if (this.__selection.length) { const selection = this.__selection[0]; - if (selection && osparc.file.FilesTree.isFile(selection)) { - const request = this.__deleteFile(selection); - if (request) { - request - .then(data => { - this.fireDataEvent("fileDeleted", data); - osparc.FlashMessenger.getInstance().logAs(this.tr("File successfully deleted"), "INFO"); - }); + if (selection) { + if (osparc.file.FilesTree.isDir(selection)) { + isFolderSelected = true; } } } + + let msg = this.tr("This operation cannot be undone."); + msg += isFolderSelected ? ("
"+this.tr("All the content of the folders will be deleted.")) : ""; + msg += "
" + this.tr("Do you want to proceed?"); + const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ + caption: this.tr("Delete"), + confirmText: this.tr("Delete"), + confirmAction: "delete" + }); + confirmationWin.center(); + confirmationWin.open(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + this.__doDeleteSelected(toBeDeleted); + } + }, this); }, - __retrieveURLAndDownloadFile: function(file) { - const fileId = file.getFileId(); - const locationId = file.getLocation(); - osparc.utils.Utils.retrieveURLAndDownload(locationId, fileId) - .then(data => { - if (data) { - osparc.DownloadLinkTracker.getInstance().downloadLinkUnattended(data.link, data.fileName); + __doDeleteSelected: function(toBeDeleted) { + const requests = []; + toBeDeleted.forEach(selection => { + if (selection) { + let request = null; + if (osparc.file.FilesTree.isFile(selection)) { + request = this.__deleteItem(selection.getFileId(), selection.getLocation()); + } else { + request = this.__deleteItem(selection.getPath(), selection.getLocation()); + } + if (request) { + requests.push(request); + } + } + }); + Promise.all(requests) + .then(datas => { + if (datas.length) { + this.fireDataEvent("fileDeleted", datas[0]); + osparc.FlashMessenger.getInstance().logAs(this.tr("Items successfully deleted"), "INFO"); } }); }, - __deleteFile: function(file) { - const fileId = file.getFileId(); - const locationId = file.getLocation(); + __deleteItem: function(itemId, locationId) { if (locationId !== 0 && locationId !== "0") { - osparc.FlashMessenger.getInstance().logAs(this.tr("Only files in simcore.s3 can be deleted")); + osparc.FlashMessenger.getInstance().logAs(this.tr("Only items in simcore.s3 can be deleted")); return null; } const dataStore = osparc.store.Data.getInstance(); - return dataStore.deleteFile(locationId, fileId); + return dataStore.deleteFile(locationId, itemId); }, } }); diff --git a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js index 5e7a4a02236..5b96a32dc8f 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js @@ -79,7 +79,7 @@ qx.Class.define("osparc.file.FileTreeItem", { itemId: { check: "String", event: "changeItemId", - apply: "_applyItemId", + apply: "__applyItemId", nullable: true }, @@ -144,7 +144,6 @@ qx.Class.define("osparc.file.FileTreeItem", { // Add lastModified const lastModifiedWidget = new qx.ui.basic.Label().set({ - width: 140, maxWidth: 140, textAlign: "right" }); @@ -162,7 +161,6 @@ qx.Class.define("osparc.file.FileTreeItem", { // Add size const sizeWidget = new qx.ui.basic.Label().set({ - width: 70, maxWidth: 70, textAlign: "right" }); @@ -175,39 +173,13 @@ qx.Class.define("osparc.file.FileTreeItem", { } }); this.addWidget(sizeWidget); - - - const permissions = osparc.data.Permissions.getInstance(); - // Add Path - const pathWidget = new qx.ui.basic.Label().set({ - width: 300, - maxWidth: 300, - textAlign: "right" - }); - this.bind("path", pathWidget, "value"); - this.addWidget(pathWidget); - permissions.bind("role", pathWidget, "visibility", { - converter: () => permissions.canDo("study.nodestree.uuid.read") ? "visible" : "excluded" - }); - - // Add NodeId - const fileIdWidget = new qx.ui.basic.Label().set({ - width: 300, - maxWidth: 300, - textAlign: "right" - }); - this.bind("fileId", fileIdWidget, "value"); - this.addWidget(fileIdWidget); - permissions.bind("role", fileIdWidget, "visibility", { - converter: () => permissions.canDo("study.nodestree.uuid.read") ? "visible" : "excluded" - }); }, - // override - _applyItemId: function(value, old) { + __applyItemId: function(value, old) { osparc.utils.Utils.setIdToWidget(this, "fileTreeItem_" + value); }, + // override _applyIcon: function(value, old) { this.base(arguments, value, old); const icon = this.getChildControl("icon", true); diff --git a/services/static-webserver/client/source/class/osparc/file/FilesTree.js b/services/static-webserver/client/source/class/osparc/file/FilesTree.js index a682bbca1c2..1d828881cae 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilesTree.js +++ b/services/static-webserver/client/source/class/osparc/file/FilesTree.js @@ -163,6 +163,10 @@ qx.Class.define("osparc.file.FilesTree", { } studyModel = this.getModel(); this.__filesToDataset("0", studyId, files, studyModel); + + // select study item + this.setSelection(new qx.data.Array([studyModel])); + this.__selectionChanged(); }); }, @@ -189,8 +193,8 @@ qx.Class.define("osparc.file.FilesTree", { } this.openNode(rootNodeModel); - const selected = new qx.data.Array([rootNodeModel]); - this.setSelection(selected); + // select node item + this.setSelection(new qx.data.Array([rootNodeModel])); this.__selectionChanged(); } else { rootModel.getChildren().removeAll(); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderContent.js b/services/static-webserver/client/source/class/osparc/file/FolderContent.js index badadbadcf2..bc6c680d926 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderContent.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderContent.js @@ -36,7 +36,7 @@ qx.Class.define("osparc.file.FolderContent", { mode: { check: ["list", "icons"], - init: "icons", + init: "list", nullable: false, event: "changeMode", apply: "__reloadFolderContent" @@ -80,7 +80,7 @@ qx.Class.define("osparc.file.FolderContent", { T_POS: { TYPE: 0, NAME: 1, - DATE: 2, + MODIFIED_DATE: 2, SIZE: 3, ID: 4 } @@ -105,9 +105,9 @@ qx.Class.define("osparc.file.FolderContent", { }); control.getTableColumnModel().setDataCellRenderer(this.self().T_POS.TYPE, new qx.ui.table.cellrenderer.Image()); control.setColumnWidth(this.self().T_POS.TYPE, 30); - control.setColumnWidth(this.self().T_POS.NAME, 360); - control.setColumnWidth(this.self().T_POS.DATE, 170); - control.setColumnWidth(this.self().T_POS.SIZE, 70); + control.setColumnWidth(this.self().T_POS.NAME, 250); + control.setColumnWidth(this.self().T_POS.MODIFIED_DATE, 125); + control.setColumnWidth(this.self().T_POS.SIZE, 80); this.bind("mode", control, "visibility", { converter: mode => mode === "list" ? "visible" : "excluded" }); @@ -144,6 +144,8 @@ qx.Class.define("osparc.file.FolderContent", { }; datas.push(data); }); + // folders first + datas.sort((a, b) => osparc.file.FilesTree.isFile(a.entry) - osparc.file.FilesTree.isFile(b.entry)); const items = []; if (this.getMode() === "list") { datas.forEach(data => { @@ -278,14 +280,14 @@ qx.Class.define("osparc.file.FolderContent", { } if (this.isMultiSelect()) { // pass all buttons that are selected - const selectedFiles = []; + const selectedItems = []; const iconsLayout = this.getChildControl("icons-layout"); iconsLayout.getChildren().forEach(btn => { - if (osparc.file.FilesTree.isFile(btn.entry) && btn.getValue()) { - selectedFiles.push(btn.entry); + if (btn.getValue() && "entry" in btn) { + selectedItems.push(btn.entry); } }); - this.__selectionChanged(selectedFiles); + this.__selectionChanged(selectedItems); } else { // unselect the other items const iconsLayout = this.getChildControl("icons-layout"); @@ -309,23 +311,23 @@ qx.Class.define("osparc.file.FolderContent", { if (e.getNativeEvent().ctrlKey) { this.setMultiSelect(true); } - const selectedFiles = []; + const selectedItems = []; const selectionRanges = table.getSelectionModel().getSelectedRanges(); selectionRanges.forEach(range => { for (let i=range.minIndex; i<=range.maxIndex; i++) { const row = table.getTableModel().getRowData(i); - if (osparc.file.FilesTree.isFile(row.entry)) { - selectedFiles.push(row.entry); + if (row && "entry" in row) { + selectedItems.push(row.entry); } } }); - this.__selectionChanged(selectedFiles); + this.__selectionChanged(selectedItems); }, this); table.addListener("cellDbltap", e => { const selectedRow = e.getRow(); - const rowData = table.getTableModel().getRowData(selectedRow); - if ("entry" in rowData) { - this.__itemDblTapped(rowData.entry); + const row = table.getTableModel().getRowData(selectedRow); + if (row && "entry" in row) { + this.__itemDblTapped(row.entry); } }, this); } diff --git a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js index af2ca15fb79..914709dbcdd 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js @@ -22,7 +22,7 @@ qx.Class.define("osparc.file.FolderViewer", { extend: qx.ui.core.Widget, - construct: function(allowMultiselection = true) { + construct: function(allowMultiSelection = true) { this.base(arguments); this._setLayout(new qx.ui.layout.VBox(10)); @@ -33,11 +33,11 @@ qx.Class.define("osparc.file.FolderViewer", { folderUpBtn.addListener("execute", () => this.fireDataEvent("folderUp", this.getFolder()), this); this.getChildControl("folder-path"); let multiSelectButton = null; - if (allowMultiselection) { + if (allowMultiSelection) { multiSelectButton = this.getChildControl("multi-select-button"); } - const gridViewButton = this.getChildControl("view-options-icons"); const listViewButton = this.getChildControl("view-options-list"); + const gridViewButton = this.getChildControl("view-options-icons"); const folderContent = this.getChildControl("folder-content"); const selectedFileLayout = this.getChildControl("selected-file-layout"); @@ -51,7 +51,7 @@ qx.Class.define("osparc.file.FolderViewer", { this.bind("folder", folderContent, "folder"); - if (allowMultiselection) { + if (allowMultiSelection) { multiSelectButton.bind("value", folderContent, "multiSelect"); folderContent.bind("multiSelect", multiSelectButton, "value"); multiSelectButton.addListener("changeValue", e => { @@ -142,12 +142,13 @@ qx.Class.define("osparc.file.FolderViewer", { header.addAt(control, 2); break; } - case "view-options-rgroup": + case "view-options-radio-group": control = new qx.ui.form.RadioGroup(); break; case "view-options-icons": { control = new qx.ui.form.ToggleButton(null, "@MaterialIcons/apps/18"); - const group = this.getChildControl("view-options-rgroup"); + osparc.utils.Utils.setIdToWidget(control, "folderGridView"); + const group = this.getChildControl("view-options-radio-group"); group.add(control); const header = this.getChildControl("header"); header.addAt(control, 3); @@ -155,7 +156,7 @@ qx.Class.define("osparc.file.FolderViewer", { } case "view-options-list": { control = new qx.ui.form.ToggleButton(null, "@MaterialIcons/reorder/18"); - const group = this.getChildControl("view-options-rgroup"); + const group = this.getChildControl("view-options-radio-group"); group.add(control); const header = this.getChildControl("header"); header.addAt(control, 4); diff --git a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js index 6512e4a459d..719b8f582df 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js @@ -62,19 +62,9 @@ qx.Class.define("osparc.info.StudyLarge", { const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - const mainHBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); - - const leftVBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); - mainHBox.add(leftVBox, { - flex: 1 - }); - - vBox.add(mainHBox); - - const extraInfo = this.__extraInfo(); - const extraInfoLayout = this.__createExtraInfo(extraInfo); - - leftVBox.add(extraInfoLayout); + const infoElements = this.__infoElements(); + const infoLayout = osparc.info.StudyUtils.infoElementsToLayout(infoElements); + vBox.add(infoLayout); let text = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + " Id"; if (this.__isTemplate) { @@ -87,7 +77,7 @@ qx.Class.define("osparc.info.StudyLarge", { allowGrowX: false }); copyIdButton.addListener("execute", () => osparc.utils.Utils.copyTextToClipboard(this.getStudy().getUuid())); - leftVBox.add(copyIdButton); + vBox.add(copyIdButton); const scrollContainer = new qx.ui.container.Scroll(); scrollContainer.add(vBox); @@ -97,8 +87,8 @@ qx.Class.define("osparc.info.StudyLarge", { }); }, - __extraInfo: function() { - const extraInfo = { + __infoElements: function() { + const infoLayout = { "TITLE": { label: this.tr("Title:"), view: osparc.info.StudyUtils.createTitle(this.getStudy()), @@ -166,7 +156,7 @@ qx.Class.define("osparc.info.StudyLarge", { this.getStudy().getQuality() && osparc.metadata.Quality.isEnabled(this.getStudy().getQuality()) ) { - extraInfo["QUALITY"] = { + infoLayout["QUALITY"] = { label: this.tr("Quality:"), view: osparc.info.StudyUtils.createQuality(this.getStudy()), action: { @@ -178,7 +168,7 @@ qx.Class.define("osparc.info.StudyLarge", { } if (osparc.product.Utils.showClassifiers()) { - extraInfo["CLASSIFIERS"] = { + infoLayout["CLASSIFIERS"] = { label: this.tr("Classifiers:"), view: osparc.info.StudyUtils.createClassifiers(this.getStudy()), action: (this.getStudy().getClassifiers().length || this.__canIWrite()) ? { @@ -192,18 +182,14 @@ qx.Class.define("osparc.info.StudyLarge", { if (!this.__isTemplate) { const pathLabel = new qx.ui.basic.Label(); pathLabel.setValue(this.getStudy().getLocationString()); - extraInfo["LOCATION"] = { + infoLayout["LOCATION"] = { label: this.tr("Location:"), view: pathLabel, action: null }; } - return extraInfo; - }, - - __createExtraInfo: function(extraInfo) { - return osparc.info.StudyUtils.createExtraInfoGrid(extraInfo); + return infoLayout; }, __createStudyId: function() { diff --git a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js index 81d4feda078..9d2ef3bf7bc 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyUtils.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyUtils.js @@ -252,7 +252,7 @@ qx.Class.define("osparc.info.StudyUtils", { return titleLayout; }, - createExtraInfoGrid: function(extraInfos) { + infoElementsToLayout: function(extraInfos) { const positions = { TITLE: { column: 0, @@ -308,18 +308,15 @@ qx.Class.define("osparc.info.StudyUtils", { }, }; - const grid = new qx.ui.layout.Grid(15, 5); + const grid1 = new qx.ui.layout.Grid(15, 5); + grid1.setColumnAlign(0, "left", "top"); + grid1.setColumnFlex(0, 1); + const mainInfoLayout = new qx.ui.container.Composite(grid1); + const grid2 = new qx.ui.layout.Grid(15, 5); - grid.setColumnAlign(0, "left", "top"); - const container = new qx.ui.container.Composite(new qx.ui.layout.VBox()); - const moreInfo = new qx.ui.container.Composite(grid); - const otherInfo = new qx.ui.container.Composite(grid2); - grid.setColumnFlex(0, 1); + const extraInfoLayout = new qx.ui.container.Composite(grid2); grid2.setColumnFlex(0, 1); - const box = this.__createSectionBox(qx.locale.Manager.tr("Details")); - const box2 = this.__createSectionBox(qx.locale.Manager.tr("Meta details")); - let row = 0; let row2 = 0; Object.keys(positions).forEach(key => { @@ -335,7 +332,7 @@ qx.Class.define("osparc.info.StudyUtils", { }); } titleLayout.add(extraInfo.view); - otherInfo.add(titleLayout, { + extraInfoLayout.add(titleLayout, { row: row2, column: gridInfo.column }); @@ -344,25 +341,30 @@ qx.Class.define("osparc.info.StudyUtils", { row2++; } else { const titleLayout = this.__titleWithEditLayout(extraInfo); - moreInfo.add(titleLayout, { + mainInfoLayout.add(titleLayout, { row, column: gridInfo.column }); row++; - moreInfo.add(extraInfo.view, { + mainInfoLayout.add(extraInfo.view, { row, column: gridInfo.column }); row++; - grid.setRowHeight(row, 5); // spacer + grid1.setRowHeight(row, 5); // spacer row++; } } }); - box.add(moreInfo); - box2.add(otherInfo); - container.addAt(box, 0); + + const container = new qx.ui.container.Composite(new qx.ui.layout.VBox()); + const box1 = this.__createSectionBox(qx.locale.Manager.tr("Details")); + box1.add(mainInfoLayout); + container.addAt(box1, 0); + + const box2 = this.__createSectionBox(qx.locale.Manager.tr("Meta details")); + box2.add(extraInfoLayout); container.addAt(box2, 1); return container; diff --git a/services/static-webserver/client/source/class/osparc/po/POCenter.js b/services/static-webserver/client/source/class/osparc/po/POCenter.js index 6141d5ffaf2..25c3fa01acd 100644 --- a/services/static-webserver/client/source/class/osparc/po/POCenter.js +++ b/services/static-webserver/client/source/class/osparc/po/POCenter.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.po.POCenter", { const miniProfile = osparc.desktop.account.MyAccount.createMiniProfileView().set({ paddingRight: 10 }); - this.addWidgetOnTopOfTheTabs(miniProfile); + this.addWidgetToTabs(miniProfile); this.__addUsersPage(); this.__addPreRegistrationPage(); 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 322dc1313bd..d592f78ea67 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -283,6 +283,7 @@ qx.Class.define("osparc.store.Data", { return true; }, + // if folder path is provided as fileUuid, it can also be deleted deleteFile: function(locationId, fileUuid) { if (!osparc.data.Permissions.getInstance().canDo("study.node.data.delete", true)) { return null; diff --git a/services/static-webserver/client/source/class/osparc/study/Utils.js b/services/static-webserver/client/source/class/osparc/study/Utils.js index 54ec6fce3e4..60b7667b22a 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -330,7 +330,7 @@ qx.Class.define("osparc.study.Utils", { canShowStudyData: function(studyData) { const blocked = this.__getBlockedState(studyData); - return [false].includes(blocked); + return ["UNKNOWN_SERVICES", false].includes(blocked); }, canShowPreview: function(studyData) { diff --git a/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js b/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js index 023fafb4dc5..547bd4ae3d2 100644 --- a/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js +++ b/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.tester.TesterCenter", { const miniProfile = osparc.desktop.account.MyAccount.createMiniProfileView().set({ paddingRight: 10 }); - this.addWidgetOnTopOfTheTabs(miniProfile); + this.addWidgetToTabs(miniProfile); this.__addSocketMessagesPage(); this.__addConsoleErrorsPage(); diff --git a/services/static-webserver/client/source/class/osparc/theme/ColorDark.js b/services/static-webserver/client/source/class/osparc/theme/ColorDark.js index 64a3784c276..47b42050a36 100644 --- a/services/static-webserver/client/source/class/osparc/theme/ColorDark.js +++ b/services/static-webserver/client/source/class/osparc/theme/ColorDark.js @@ -122,28 +122,6 @@ qx.Theme.define("osparc.theme.ColorDark", { "tooltip": "flash_message_bg", "tooltip-text": "text", - // table - "table-header": "background-main", - "table-header-foreground": "c09", - "table-header-border": "c07", - "table-focus-indicator": "background-main-5", - - // used in table code - "table-header-cell": "background-main", - "table-row-background-even": "background-main", - "table-row-background-odd": "background-main", - "table-row-background-focused": "background-main-1", - "table-row-background-focused-selected": "background-main-2", - "table-row-background-selected": "background-main-2", - - // foreground - "table-row-selected": "c12", - "table-row": "c09", - - // table grid color - "table-row-line": "background-main", - "table-column-line": "background-main", - // used in progressive code "progressive-table-header": "c08", "progressive-table-row-background-even": "background-main", diff --git a/services/static-webserver/client/source/class/osparc/theme/ColorLight.js b/services/static-webserver/client/source/class/osparc/theme/ColorLight.js index 6b798200e18..629e75ccef5 100644 --- a/services/static-webserver/client/source/class/osparc/theme/ColorLight.js +++ b/services/static-webserver/client/source/class/osparc/theme/ColorLight.js @@ -124,28 +124,6 @@ qx.Theme.define("osparc.theme.ColorLight", { "tooltip-text": "text", - // table - "table-header": "background-main", - "table-header-foreground": "c09", - "table-header-border": "c07", - "table-focus-indicator": "background-main-5", - - // used in table code - "table-header-cell": "background-main", - "table-row-background-even": "background-main", - "table-row-background-odd": "background-main", - "table-row-background-focused": "background-main-1", - "table-row-background-focused-selected": "background-main-2", - "table-row-background-selected": "background-main-2", - - // foreground - "table-row-selected": "c12", - "table-row": "c09", - - // table grid color - "table-row-line": "background-main", - "table-column-line": "background-main", - // used in progressive code "progressive-table-header": "c08", "progressive-table-row-background-even": "background-main", diff --git a/services/static-webserver/client/source/class/osparc/theme/mixin/Color.js b/services/static-webserver/client/source/class/osparc/theme/mixin/Color.js index 95883e8284f..f1224d1fe82 100644 --- a/services/static-webserver/client/source/class/osparc/theme/mixin/Color.js +++ b/services/static-webserver/client/source/class/osparc/theme/mixin/Color.js @@ -54,6 +54,29 @@ qx.Theme.define("osparc.theme.mixin.Color", { "logger-warning-message": "warning-yellow", "logger-error-message": "failed-red", - "workbench-edge-selected": "busy-orange" + "workbench-edge-selected": "busy-orange", + + + // table + "table-header": "transparent", + "table-header-foreground": "text", // text color + "table-header-border": "text", // header underline + "table-header-cell": "transparent", + + // used in table code + "table-focus-indicator": "transparent", + "table-row-background-even": "transparent", + "table-row-background-odd": "transparent", + "table-row-background-focused": "transparent", + "table-row-background-focused-selected": "background-main-2", + "table-row-background-selected": "background-main-2", + + // foreground + "table-row-selected": "text", + "table-row": "text", + + // table grid color + "table-row-line": "transparent", + "table-column-line": "transparent", } }); diff --git a/services/static-webserver/client/source/class/osparc/ui/window/TabbedView.js b/services/static-webserver/client/source/class/osparc/ui/window/TabbedView.js index e3c75167676..6706b152f92 100644 --- a/services/static-webserver/client/source/class/osparc/ui/window/TabbedView.js +++ b/services/static-webserver/client/source/class/osparc/ui/window/TabbedView.js @@ -79,7 +79,7 @@ qx.Class.define("osparc.ui.window.TabbedView", { return control || this.base(arguments, id); }, - addWidgetOnTopOfTheTabs: function(widget) { + addWidgetToTabs: function(widget) { this.getChildControl("tabs-view").getChildControl("bar").add(widget); }, diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js index 451a86083a1..edad4b3a2d7 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js @@ -25,7 +25,7 @@ qx.Class.define("osparc.vipMarket.Market", { paddingRight: 10, minWidth: 150, }); - this.addWidgetOnTopOfTheTabs(miniWallet); + this.addWidgetToTabs(miniWallet); const store = osparc.store.Store.getInstance(); const contextWallet = store.getContextWallet(); diff --git a/services/static-webserver/client/source/class/osparc/widget/ProgressSequence.js b/services/static-webserver/client/source/class/osparc/widget/ProgressSequence.js index d9c3e257d81..09cadba98af 100644 --- a/services/static-webserver/client/source/class/osparc/widget/ProgressSequence.js +++ b/services/static-webserver/client/source/class/osparc/widget/ProgressSequence.js @@ -25,7 +25,8 @@ qx.Class.define("osparc.widget.ProgressSequence", { this.set({ backgroundColor: "window-popup-background", - paddingBottom: 8 + paddingBottom: 8, + minWidth: 400, }); this.__initLayout(title); diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js b/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js similarity index 86% rename from services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js rename to services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js index 397477d1d94..2626265b0cf 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeDataManager.js +++ b/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js @@ -25,12 +25,12 @@ * Here is a little example of how to use the widget. * *
- *   let dataManager = new osparc.widget.NodeDataManager(null, nodeId);
+ *   let dataManager = new osparc.widget.StudyDataManager(null, nodeId);
  *   this.getRoot().add(dataManager);
  * 
*/ -qx.Class.define("osparc.widget.NodeDataManager", { +qx.Class.define("osparc.widget.StudyDataManager", { extend: qx.ui.core.Widget, /** @@ -58,6 +58,16 @@ qx.Class.define("osparc.widget.NodeDataManager", { this.__reloadTree(); }, + statics: { + popUpInWindow: function(studyId, nodeId, title) { + const studyDataManager = new osparc.widget.StudyDataManager(studyId, nodeId); + if (!title) { + title = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + qx.locale.Manager.tr(" Files"); + } + return osparc.ui.window.Window.popUpInWindow(studyDataManager, title, osparc.dashboard.ResourceDetails.WIDTH, osparc.dashboard.ResourceDetails.HEIGHT); + }, + }, + properties: { studyId: { check: "String", diff --git a/tests/e2e-playwright/tests/sleepers/test_sleepers.py b/tests/e2e-playwright/tests/sleepers/test_sleepers.py index 4415fcf3e49..0940b37609d 100644 --- a/tests/e2e-playwright/tests/sleepers/test_sleepers.py +++ b/tests/e2e-playwright/tests/sleepers/test_sleepers.py @@ -67,6 +67,7 @@ def _get_expected_file_names_for_version(version: Version) -> list[str]: ) def _get_file_names(page: Page) -> list[str]: file_names_found = [] + page.get_by_test_id("folderGridView").click() for file in page.get_by_test_id("FolderViewerItem").all(): file_name = file.text_content() assert file_name diff --git a/tests/e2e/tutorials/tutorialBase.js b/tests/e2e/tutorials/tutorialBase.js index 6899d3bcb26..76c7edcb3de 100644 --- a/tests/e2e/tutorials/tutorialBase.js +++ b/tests/e2e/tutorials/tutorialBase.js @@ -484,6 +484,7 @@ class TutorialBase { async __checkNItemsInFolder(fileNames, openOutputsFolder = false) { await this.takeScreenshot("checkNodeOutputs_before"); + await this.waitAndClick("folderGridView"); console.log("N items in folder. Expected:", fileNames); if (openOutputsFolder) { const itemTexts = await this.__page.$$eval('[osparc-test-id="FolderViewerItem"]', @@ -506,8 +507,7 @@ class TutorialBase { } if (outputsFound) { await this.takeScreenshot("outputs_folder"); - } - else { + } else { throw ("outputs folder not found"); } } diff --git a/tests/e2e/utils/auto.js b/tests/e2e/utils/auto.js index 4e999201736..9af239e3ad9 100644 --- a/tests/e2e/utils/auto.js +++ b/tests/e2e/utils/auto.js @@ -363,6 +363,7 @@ async function openNodeFilesAppMode(page) { async function checkDataProducedByNode(page, nFiles = 1) { console.log("checking Data produced by Node. Expecting", nFiles, "file(s)"); + await utils.waitAndClick(page, '[osparc-test-id="folderGridView"]'); const iconsContent = await page.waitForSelector('[osparc-test-id="FolderViewerIconsContent"]', { timeout: 5000 });