diff --git a/.codecov.yml b/.codecov.yml index eb2e6697348..f4a4f9cbcf4 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -10,10 +10,10 @@ flag_management: statuses: - type: project target: auto - threshold: 2% + threshold: 5% - type: patch target: auto - threshold: 2% + threshold: 5% component_management: @@ -22,7 +22,7 @@ component_management: statuses: - type: project target: auto - threshold: 2% + threshold: 5% branches: - "!master" individual_components: @@ -116,12 +116,12 @@ coverage: project: default: informational: true - threshold: 2% + threshold: 5% patch: default: informational: true - threshold: 2% + threshold: 5% comment: layout: "header,diff,flags,components,footer" diff --git a/packages/postgres-database/requirements/_test.in b/packages/postgres-database/requirements/_test.in index 3249c9c02b2..d0b7af019df 100644 --- a/packages/postgres-database/requirements/_test.in +++ b/packages/postgres-database/requirements/_test.in @@ -10,6 +10,7 @@ --constraint _migration.txt aiopg[sa] +arrow coverage faker pytest diff --git a/packages/postgres-database/requirements/_test.txt b/packages/postgres-database/requirements/_test.txt index 49636a365c3..6b6c490ce72 100644 --- a/packages/postgres-database/requirements/_test.txt +++ b/packages/postgres-database/requirements/_test.txt @@ -1,5 +1,7 @@ aiopg==1.4.0 # via -r requirements/_test.in +arrow==1.3.0 + # via -r requirements/_test.in async-timeout==4.0.3 # via # -c requirements/_base.txt @@ -52,7 +54,9 @@ pytest-instafail==0.5.0 pytest-runner==6.0.1 # via -r requirements/_test.in python-dateutil==2.9.0.post0 - # via faker + # via + # arrow + # faker pyyaml==6.0.2 # via # -c requirements/../../../requirements/constraints.txt @@ -72,6 +76,8 @@ types-docker==7.1.0.20240827 # via -r requirements/_test.in types-psycopg2==2.9.21.20241019 # via -r requirements/_test.in +types-python-dateutil==2.9.0.20241206 + # via arrow types-requests==2.32.0.20241016 # via types-docker typing-extensions==4.12.2 diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/52a0e8148dd5_remove_submit_timestamp.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/52a0e8148dd5_remove_submit_timestamp.py new file mode 100644 index 00000000000..8589578abe7 --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/52a0e8148dd5_remove_submit_timestamp.py @@ -0,0 +1,28 @@ +"""remove submit timestamp + +Revision ID: 52a0e8148dd5 +Revises: 77ac824a77ff +Create Date: 2024-12-16 14:55:15.114923+00:00 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '52a0e8148dd5' +down_revision = '77ac824a77ff' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('comp_tasks', 'submit') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('comp_tasks', sa.Column('submit', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True)) + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py b/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py index af5dc451cc3..8389495d9b6 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/comp_tasks.py @@ -77,10 +77,6 @@ class NodeClass(enum.Enum): nullable=True, doc="current progress of the task if available", ), - # utc timestamps for submission/start/end - sa.Column( - "submit", sa.DateTime(timezone=True), doc="UTC timestamp for task submission" - ), sa.Column( "start", sa.DateTime(timezone=True), doc="UTC timestamp when task started" ), diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/faker_factories.py b/packages/pytest-simcore/src/pytest_simcore/helpers/faker_factories.py index d4418a5ef81..803987b3b8a 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/faker_factories.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/faker_factories.py @@ -16,10 +16,11 @@ import json import random from collections.abc import Callable -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any, Final from uuid import uuid4 +import arrow import faker from faker import Faker @@ -182,7 +183,7 @@ def fake_task_factory(first_internal_id=1) -> Callable: _index_in_sequence = itertools.count(start=first_internal_id) def fake_task(**overrides) -> dict[str, Any]: - t0 = datetime.utcnow() + t0 = arrow.utcnow().datetime data = { "project_id": uuid4(), "node_id": uuid4(), @@ -193,7 +194,6 @@ def fake_task(**overrides) -> dict[str, Any]: "outputs": json.dumps({}), "image": json.dumps({}), "state": random.choice(_get_comp_pipeline_test_states()), - "submit": t0, "start": t0 + timedelta(seconds=1), "end": t0 + timedelta(minutes=5), } @@ -251,7 +251,7 @@ def random_product( def utcnow() -> datetime: - return datetime.now(tz=timezone.utc) + return datetime.now(tz=UTC) def random_payment_method( diff --git a/services/director-v2/src/simcore_service_director_v2/api/routes/computations.py b/services/director-v2/src/simcore_service_director_v2/api/routes/computations.py index f0b6e635ac7..707d7a8cc1e 100644 --- a/services/director-v2/src/simcore_service_director_v2/api/routes/computations.py +++ b/services/director-v2/src/simcore_service_director_v2/api/routes/computations.py @@ -1,4 +1,4 @@ -""" CRUD operations on a "computation" resource +"""CRUD operations on a "computation" resource A computation is a resource that represents a running pipeline of computational services in a give project Therefore, @@ -15,7 +15,6 @@ # pylint: disable=too-many-arguments # pylint: disable=too-many-statements - import contextlib import logging from typing import Annotated, Any, Final @@ -75,7 +74,6 @@ compute_pipeline_details, compute_pipeline_started_timestamp, compute_pipeline_stopped_timestamp, - compute_pipeline_submitted_timestamp, create_complete_dag, create_complete_dag_from_tasks, create_minimal_computational_graph_based_on_selection, @@ -396,9 +394,7 @@ async def create_computation( # noqa: PLR0913 # pylint: disable=too-many-positi stopped=compute_pipeline_stopped_timestamp( minimal_computational_dag, comp_tasks ), - submitted=compute_pipeline_submitted_timestamp( - minimal_computational_dag, comp_tasks - ), + submitted=last_run.created if last_run else None, ) except ProjectNotFoundError as e: @@ -498,7 +494,7 @@ async def get_computation( result=None, started=compute_pipeline_started_timestamp(pipeline_dag, all_tasks), stopped=compute_pipeline_stopped_timestamp(pipeline_dag, all_tasks), - submitted=compute_pipeline_submitted_timestamp(pipeline_dag, all_tasks), + submitted=last_run.created if last_run else None, ) @@ -572,7 +568,7 @@ async def stop_computation( result=None, started=compute_pipeline_started_timestamp(pipeline_dag, tasks), stopped=compute_pipeline_stopped_timestamp(pipeline_dag, tasks), - submitted=compute_pipeline_submitted_timestamp(pipeline_dag, tasks), + submitted=last_run.created if last_run else None, ) except ProjectNotFoundError as e: diff --git a/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py b/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py index 4bf9e1e30af..2b646cf424e 100644 --- a/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py +++ b/services/director-v2/src/simcore_service_director_v2/models/comp_tasks.py @@ -128,7 +128,6 @@ class CompTaskAtDB(BaseModel): description="the hex digest of the resolved inputs +outputs hash at the time when the last outputs were generated", ) image: Image - submit: dt.datetime start: dt.datetime | None = None end: dt.datetime | None = None state: RunningState @@ -163,7 +162,7 @@ def _convert_state_from_state_type_enum_if_needed(cls, v): return RunningState(DB_TO_RUNNING_STATE[StateType(v)]) return v - @field_validator("start", "end", "submit") + @field_validator("start", "end") @classmethod def _ensure_utc(cls, v: dt.datetime | None) -> dt.datetime | None: if v is not None and v.tzinfo is None: @@ -228,7 +227,6 @@ def to_db_model(self, **exclusion_rules) -> dict[str, Any]: } }, "image": image_example, - "submit": "2021-03-01 13:07:34.19161", "node_class": "INTERACTIVE", "state": "NOT_STARTED", "progress": 0.44, diff --git a/services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_tasks/_utils.py b/services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_tasks/_utils.py index 2619d9ce98f..b703691926f 100644 --- a/services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_tasks/_utils.py +++ b/services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_tasks/_utils.py @@ -145,12 +145,12 @@ async def _get_node_infos( None, ) - result: tuple[ServiceMetaDataPublished, ServiceExtras, SimcoreServiceLabels] = ( - await asyncio.gather( - _get_service_details(catalog_client, user_id, product_name, node), - director_client.get_service_extras(node.key, node.version), - director_client.get_service_labels(node), - ) + result: tuple[ + ServiceMetaDataPublished, ServiceExtras, SimcoreServiceLabels + ] = await asyncio.gather( + _get_service_details(catalog_client, user_id, product_name, node), + director_client.get_service_extras(node.key, node.version), + director_client.get_service_labels(node), ) return result @@ -246,9 +246,9 @@ async def _get_pricing_and_hardware_infos( return pricing_info, hardware_info -_RAM_SAFE_MARGIN_RATIO: Final[float] = ( - 0.1 # NOTE: machines always have less available RAM than advertised -) +_RAM_SAFE_MARGIN_RATIO: Final[ + float +] = 0.1 # NOTE: machines always have less available RAM than advertised _CPUS_SAFE_MARGIN: Final[float] = 0.1 @@ -266,11 +266,11 @@ async def _update_project_node_resources_from_hardware_info( if not hardware_info.aws_ec2_instances: return try: - unordered_list_ec2_instance_types: list[EC2InstanceTypeGet] = ( - await get_instance_type_details( - rabbitmq_rpc_client, - instance_type_names=set(hardware_info.aws_ec2_instances), - ) + unordered_list_ec2_instance_types: list[ + EC2InstanceTypeGet + ] = await get_instance_type_details( + rabbitmq_rpc_client, + instance_type_names=set(hardware_info.aws_ec2_instances), ) assert unordered_list_ec2_instance_types # nosec @@ -439,7 +439,6 @@ async def generate_tasks_list_from_project( inputs=node.inputs, outputs=node.outputs, image=image, - submit=arrow.utcnow().datetime, state=task_state, internal_id=internal_id, node_class=to_node_class(node.key), diff --git a/services/director-v2/src/simcore_service_director_v2/utils/dags.py b/services/director-v2/src/simcore_service_director_v2/utils/dags.py index 07d60e82fd5..2b8593fce07 100644 --- a/services/director-v2/src/simcore_service_director_v2/utils/dags.py +++ b/services/director-v2/src/simcore_service_director_v2/utils/dags.py @@ -217,17 +217,6 @@ def compute_pipeline_stopped_timestamp( return pipeline_stopped_at -def compute_pipeline_submitted_timestamp( - pipeline_dag: nx.DiGraph, comp_tasks: list[CompTaskAtDB] -) -> datetime.datetime | None: - if not pipeline_dag.nodes: - return None - node_id_to_comp_task: dict[NodeIDStr, CompTaskAtDB] = { - NodeIDStr(f"{task.node_id}"): task for task in comp_tasks - } - return max(node_id_to_comp_task[node_id].submit for node_id in pipeline_dag.nodes) - - async def compute_pipeline_details( complete_dag: nx.DiGraph, pipeline_dag: nx.DiGraph, comp_tasks: list[CompTaskAtDB] ) -> PipelineDetails: diff --git a/services/director-v2/tests/mocks/fake_task.json b/services/director-v2/tests/mocks/fake_task.json index 00a9dfe3501..57d7a4c2837 100644 --- a/services/director-v2/tests/mocks/fake_task.json +++ b/services/director-v2/tests/mocks/fake_task.json @@ -50,7 +50,6 @@ "requires_gpu": false, "requires_mpi": true }, - "submit": "1994-11-10T19:23:02.115Z", "state": "PUBLISHED", "internal_id": 21107840, "node_class": "COMPUTATIONAL", diff --git a/services/director-v2/tests/unit/test_utils_dags.py b/services/director-v2/tests/unit/test_utils_dags.py index 11975ac9e88..0fc17030a2d 100644 --- a/services/director-v2/tests/unit/test_utils_dags.py +++ b/services/director-v2/tests/unit/test_utils_dags.py @@ -485,9 +485,8 @@ def pipeline_test_params( state=RunningState.NOT_STARTED, internal_id=3, node_class=NodeClass.COMPUTATIONAL, - submit=datetime.datetime.now(tz=datetime.timezone.utc), - created=datetime.datetime.now(tz=datetime.timezone.utc), - modified=datetime.datetime.now(tz=datetime.timezone.utc), + created=datetime.datetime.now(tz=datetime.UTC), + modified=datetime.datetime.now(tz=datetime.UTC), last_heartbeat=None, progress=1.00, ) @@ -536,9 +535,8 @@ def pipeline_test_params( state=RunningState.NOT_STARTED, internal_id=3, node_class=NodeClass.COMPUTATIONAL, - submit=datetime.datetime.now(tz=datetime.timezone.utc), - created=datetime.datetime.now(tz=datetime.timezone.utc), - modified=datetime.datetime.now(tz=datetime.timezone.utc), + created=datetime.datetime.now(tz=datetime.UTC), + modified=datetime.datetime.now(tz=datetime.UTC), last_heartbeat=None, ), CompTaskAtDB.model_construct( @@ -550,9 +548,8 @@ def pipeline_test_params( state=RunningState.NOT_STARTED, internal_id=3, node_class=NodeClass.COMPUTATIONAL, - submit=datetime.datetime.now(tz=datetime.timezone.utc), - created=datetime.datetime.now(tz=datetime.timezone.utc), - modified=datetime.datetime.now(tz=datetime.timezone.utc), + created=datetime.datetime.now(tz=datetime.UTC), + modified=datetime.datetime.now(tz=datetime.UTC), last_heartbeat=None, ), CompTaskAtDB.model_construct( @@ -564,9 +561,8 @@ def pipeline_test_params( state=RunningState.NOT_STARTED, internal_id=3, node_class=NodeClass.COMPUTATIONAL, - submit=datetime.datetime.now(tz=datetime.timezone.utc), - created=datetime.datetime.now(tz=datetime.timezone.utc), - modified=datetime.datetime.now(tz=datetime.timezone.utc), + created=datetime.datetime.now(tz=datetime.UTC), + modified=datetime.datetime.now(tz=datetime.UTC), last_heartbeat=None, progress=1.00, ), diff --git a/services/director-v2/tests/unit/with_dbs/comp_scheduler/test_api_route_computations.py b/services/director-v2/tests/unit/with_dbs/comp_scheduler/test_api_route_computations.py index 4c8ed5f4b78..2d076c3fdf0 100644 --- a/services/director-v2/tests/unit/with_dbs/comp_scheduler/test_api_route_computations.py +++ b/services/director-v2/tests/unit/with_dbs/comp_scheduler/test_api_route_computations.py @@ -258,7 +258,11 @@ def _mocked_services_details( ).isoformat() } - data = {**ServiceGet.model_config["json_schema_extra"]["examples"][0], **data_published, **deprecated} # type: ignore + data = { + **ServiceGet.model_config["json_schema_extra"]["examples"][0], + **data_published, + **deprecated, + } # type: ignore payload = ServiceGet.model_validate(data) @@ -354,7 +358,6 @@ def _mocked_get_pricing_unit(request, pricing_plan_id: int) -> httpx.Response: assert_all_called=False, assert_all_mocked=True, ) as respx_mock: - respx_mock.get( re.compile( r"services/(?Psimcore/services/(comp|dynamic|frontend)/[^/]+)/(?P[^\.]+.[^\.]+.[^/\?]+)/pricing-plan.+" @@ -915,13 +918,7 @@ async def test_get_computation_from_not_started_computation_task( stopped=None, submitted=None, ) - _CHANGED_FIELDS = {"submitted"} - assert returned_computation.model_dump( - exclude=_CHANGED_FIELDS - ) == expected_computation.model_dump(exclude=_CHANGED_FIELDS) - assert returned_computation.model_dump( - include=_CHANGED_FIELDS - ) != expected_computation.model_dump(include=_CHANGED_FIELDS) + assert returned_computation == expected_computation async def test_get_computation_from_published_computation_task( diff --git a/services/director-v2/tests/unit/with_dbs/conftest.py b/services/director-v2/tests/unit/with_dbs/conftest.py index 703686d2526..9b0d03e6eed 100644 --- a/services/director-v2/tests/unit/with_dbs/conftest.py +++ b/services/director-v2/tests/unit/with_dbs/conftest.py @@ -118,7 +118,6 @@ async def _( ), "node_class": to_node_class(node_data.key), "internal_id": internal_id + 1, - "submit": datetime.datetime.now(datetime.UTC), "job_id": generate_dask_job_id( service_key=node_data.key, service_version=node_data.version, diff --git a/services/static-webserver/client/source/class/osparc/auth/Data.js b/services/static-webserver/client/source/class/osparc/auth/Data.js index 306d3032558..2a4b27a9646 100644 --- a/services/static-webserver/client/source/class/osparc/auth/Data.js +++ b/services/static-webserver/client/source/class/osparc/auth/Data.js @@ -147,6 +147,17 @@ qx.Class.define("osparc.auth.Data", { return this.getUsername(); }, + getFullName: function() { + let name = ""; + if (this.getFirstName()) { + name += this.getFirstName(); + } + if (this.getLastName()) { + name += " " + this.getLastName(); + } + return name; + }, + getFriendlyRole: function() { const role = this.getRole(); let friendlyRole = role.replace(/_/g, " "); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js b/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js index 83aed499cb4..b67d7669de2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js @@ -161,6 +161,7 @@ qx.Class.define("osparc.dashboard.DragDropHelpers", { const folderOrigin = event.getData("osparc-moveFolder")["folderOrigin"]; const folderToFolderData = { folderId: folderOrigin.getFolderId(), + workspaceId: folderOrigin.getWorkspaceId(), destWorkspaceId, destFolderId, }; @@ -169,11 +170,76 @@ qx.Class.define("osparc.dashboard.DragDropHelpers", { }, }, + trashStudy: { + dragOver: function(event) { + let compatible = false; + const studyDataOrigin = event.getData("osparc-moveStudy")["studyDataOrigin"]; + const workspaceIdOrigin = studyDataOrigin["workspaceId"]; + const workspaceOrigin = osparc.store.Workspaces.getInstance().getWorkspace(workspaceIdOrigin); + // Compatibility checks: + // - My Workspace -> Trash (0) + // - Delete on Study + // - Shared Workspace -> Trash (1) + // - Delete on Shared Workspace + if (workspaceIdOrigin === null) { // (0) + compatible = osparc.data.model.Study.canIDelete(studyDataOrigin["accessRights"]); + } else if (workspaceIdOrigin !== null) { // (1) + compatible = workspaceOrigin.getMyAccessRights()["delete"]; + } + + if (!compatible) { + // do not allow + event.preventDefault(); + } + + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.setDropAllowed(compatible); + }, + + drop: function(event) { + return event.getData("osparc-moveStudy")["studyDataOrigin"]; + }, + }, + + trashFolder: { + dragOver: function(event) { + let compatible = false; + const folderOrigin = event.getData("osparc-moveFolder")["folderOrigin"]; + const workspaceIdOrigin = folderOrigin.getWorkspaceId(); + const workspaceOrigin = osparc.store.Workspaces.getInstance().getWorkspace(workspaceIdOrigin); + // Compatibility checks: + // - My Workspace -> Trash (0) + // - Yes + // - Shared Workspace -> Trash (1) + // - Delete on Shared Workspace + if (workspaceIdOrigin === null) { // (0) + compatible = true; + } else if (workspaceIdOrigin !== null) { // (1) + compatible = workspaceOrigin.getMyAccessRights()["delete"]; + } + + if (!compatible) { + // do not allow + event.preventDefault(); + } + + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.setDropAllowed(compatible); + }, + + drop: function(event) { + const folderOrigin = event.getData("osparc-moveFolder")["folderOrigin"]; + return folderOrigin.getFolderId(); + }, + }, + dragLeave: function(item) { const dragWidget = osparc.dashboard.DragWidget.getInstance(); dragWidget.setDropAllowed(false); - item.getChildControl("icon").resetTextColor(); + if (item) { + item.getChildControl("icon").resetTextColor(); + } }, dragEnd: function(draggedItem) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js b/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js index 64a1c188f1f..4685f93caaf 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js @@ -27,10 +27,10 @@ qx.Class.define("osparc.dashboard.DragWidget", { })); this.set({ + appearance: "strong-ui", opacity: 0.9, padding: 10, zIndex: 1000, - backgroundColor: "strong-main", decorator: "rounded", visibility: "excluded", }); @@ -71,7 +71,7 @@ qx.Class.define("osparc.dashboard.DragWidget", { __dropAllowed: function(allowed) { this.getChildControl("allowed-icon").set({ source: allowed ? "@FontAwesome5Solid/check/14" : "@FontAwesome5Solid/times/14", - textColor: allowed ? "text" : "danger-red", + textColor: allowed ? "default-button-text" : "danger-red", }); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index 4d11a423c34..f496b14501a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -272,7 +272,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { menu.addSeparator(); const trashButton = new qx.ui.menu.Button(this.tr("Trash"), "@FontAwesome5Solid/trash/12"); - trashButton.addListener("execute", () => this.__trashFolderRequested(), this); + trashButton.addListener("execute", () => this.fireDataEvent("trashFolderRequested", this.getFolderId()), this); menu.add(trashButton); } else if (studyBrowserContext === "trash") { const restoreButton = new qx.ui.menu.Button(this.tr("Restore"), "@MaterialIcons/restore_from_trash/16"); @@ -325,24 +325,6 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { folderEditor.addListener("cancel", () => win.close()); }, - __trashFolderRequested: function() { - const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays(); - let msg = this.tr("Are you sure you want to move the Folder and all its content to the trash?"); - msg += "

" + this.tr("It will be permanently deleted after ") + trashDays + " days."; - const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ - caption: this.tr("Move to Trash"), - confirmText: this.tr("Move to Trash"), - confirmAction: "delete" - }); - confirmationWin.center(); - confirmationWin.open(); - confirmationWin.addListener("close", () => { - if (confirmationWin.getConfirmed()) { - this.fireDataEvent("trashFolderRequested", this.getFolderId()); - } - }, this); - }, - __deleteFolderRequested: function() { const msg = this.tr("Are you sure you want to delete") + " " + this.getTitle() + "?"; const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js index 0e0d92b61b6..4f1c9c9d736 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js @@ -171,7 +171,7 @@ qx.Class.define("osparc.dashboard.NewStudies", { const newPlanButton = new osparc.dashboard.GridButtonNew(title, desc); newPlanButton.setCardKey(templateInfo.idToWidget); osparc.utils.Utils.setIdToWidget(newPlanButton, templateInfo.idToWidget); - newPlanButton.addListener("execute", () => newStudyClicked()); + newPlanButton.addListener("tap", () => newStudyClicked()); return newPlanButton; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js index 097247cc847..ad896f44cfd 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js @@ -35,6 +35,8 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { events: { "trashContext": "qx.event.type.Event", + "trashStudyRequested": "qx.event.type.Data", + "trashFolderRequested": "qx.event.type.Data", "changeSharedWith": "qx.event.type.Data", "changeSelectedTags": "qx.event.type.Data", "changeServiceType": "qx.event.type.Data" @@ -114,9 +116,36 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { } }); this.evaluateTrashEmpty(); + this.__attachDropHandlers(trashButton); return trashButton; }, + __attachDropHandlers: function(trashButton) { + trashButton.setDroppable(true); + + trashButton.addListener("dragover", e => { + if (e.supportsType("osparc-moveStudy")) { + osparc.dashboard.DragDropHelpers.trashStudy.dragOver(e); + } else if (e.supportsType("osparc-moveFolder")) { + osparc.dashboard.DragDropHelpers.trashFolder.dragOver(e); + } + }); + + trashButton.addListener("dragleave", () => { + osparc.dashboard.DragDropHelpers.dragLeave(); + }); + + trashButton.addListener("drop", e => { + if (e.supportsType("osparc-moveStudy")) { + const studyData = osparc.dashboard.DragDropHelpers.trashStudy.drop(e); + this.fireDataEvent("trashStudyRequested", studyData); + } else if (e.supportsType("osparc-moveFolder")) { + const folderId = osparc.dashboard.DragDropHelpers.trashFolder.drop(e); + this.fireDataEvent("trashFolderRequested", folderId); + } + }); + }, + evaluateTrashEmpty: function() { const studiesParams = { url: { 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 081d63a2a96..397f2682acb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -555,7 +555,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__reloadFolders(); }, - __showMoveToWorkspaceWarningMessage: function() { + __showMoveToDifferentWorkspaceWarningMessage: function() { const msg = this.tr("The permissions will be taken from the new workspace."); const win = new osparc.ui.window.Confirmation(msg).set({ caption: this.tr("Move"), @@ -565,13 +565,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return win; }, - __doMoveFolder: function(folderId, destWorkspaceId, destFolderId) { - osparc.store.Folders.getInstance().moveFolderToWorkspace(folderId, destWorkspaceId) // first move to workspace - .then(() => osparc.store.Folders.getInstance().moveFolderToFolder(folderId, destFolderId)) // then move to folder - .then(() => this.__reloadFolders()) - .catch(err => console.error(err)); - }, - _moveFolderToRequested: function(folderId) { const currentWorkspaceId = this.getCurrentWorkspaceId(); const currentFolderId = this.getCurrentFolderId(); @@ -583,30 +576,35 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const data = e.getData(); const destWorkspaceId = data["workspaceId"]; const destFolderId = data["folderId"]; - if (destWorkspaceId === currentWorkspaceId) { - this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); - } else { - const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); - confirmationWin.addListener("close", () => { - if (confirmationWin.getConfirmed()) { - this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); - } - }, this); - } + this._folderToFolderRequested(folderId, currentWorkspaceId, destWorkspaceId, destFolderId); }); moveFolderTo.addListener("cancel", () => win.close()); }, - _folderToFolderRequested: function(data) { - const { - folderId, - destWorkspaceId, - destFolderId, - } = data; - this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); + __doMoveFolder: function(folderId, destWorkspaceId, destFolderId) { + osparc.store.Folders.getInstance().moveFolderToWorkspace(folderId, destWorkspaceId) // first move to workspace + .then(() => osparc.store.Folders.getInstance().moveFolderToFolder(folderId, destFolderId)) // then move to folder + .then(() => this.__reloadFolders()) + .catch(err => { + console.error(err); + osparc.FlashMessenger.getInstance().logAs(err, "ERROR"); + }); }, - _trashFolderRequested: function(folderId) { + _folderToFolderRequested: function(folderId, workspaceId, destWorkspaceId, destFolderId) { + if (destWorkspaceId === workspaceId) { + this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); + } else { + const confirmationWin = this.__showMoveToDifferentWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); + } + }, this); + } + }, + + __folderToTrash: function(folderId) { osparc.store.Folders.getInstance().trashFolder(folderId, this.getCurrentWorkspaceId()) .then(() => { this.__reloadFolders(); @@ -617,7 +615,25 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .catch(err => { console.error(err); osparc.FlashMessenger.getInstance().logAs(err, "ERROR"); - }) + }); + }, + + _trashFolderRequested: function(folderId) { + const trashDays = osparc.store.StaticInfo.getInstance().getTrashRetentionDays(); + let msg = this.tr("Are you sure you want to move the Folder and all its content to the trash?"); + msg += "

" + this.tr("It will be permanently deleted after ") + trashDays + " days."; + const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ + caption: this.tr("Move to Trash"), + confirmText: this.tr("Move to Trash"), + confirmAction: "warning", + }); + confirmationWin.center(); + confirmationWin.open(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + this.__folderToTrash(folderId); + } + }, this); }, _untrashFolderRequested: function(folder) { @@ -1162,10 +1178,28 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __connectDropHandlers: function() { const workspacesAndFoldersTree = this._resourceFilter.getWorkspacesAndFoldersTree(); workspacesAndFoldersTree.addListener("studyToFolderRequested", e => { - this._studyToFolderRequested(e.getData()); + const { + studyData, + destWorkspaceId, + destFolderId, + } = e.getData(); + this._moveStudyToFolderReqested(studyData, destWorkspaceId, destFolderId); }); workspacesAndFoldersTree.addListener("folderToFolderRequested", e => { - this._folderToFolderRequested(e.getData()); + const { + folderId, + workspaceId, + destWorkspaceId, + destFolderId, + } = e.getData(); + this._folderToFolderRequested(folderId, workspaceId, destWorkspaceId, destFolderId); + }); + + this._resourceFilter.addListener("trashStudyRequested", e => { + this.__trashStudyRequested(e.getData()); + }); + this._resourceFilter.addListener("trashFolderRequested", e => { + this._trashFolderRequested(e.getData()); }); }, @@ -1251,7 +1285,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (destWorkspaceId === currentWorkspaceId) { moveStudies(); } else { - const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); + const confirmationWin = this.__showMoveToDifferentWorkspaceWarningMessage(); confirmationWin.addListener("close", () => { if (confirmationWin.getConfirmed()) { moveStudies(); @@ -1652,6 +1686,19 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, + _moveStudyToFolderReqested: function(studyData, destWorkspaceId, destFolderId) { + if (studyData["workspaceId"] === destWorkspaceId) { + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); + } else { + const confirmationWin = this.__showMoveToDifferentWorkspaceWarningMessage(); + confirmationWin.addListener("close", () => { + if (confirmationWin.getConfirmed()) { + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); + } + }, this); + } + }, + __getMoveStudyToMenuButton: function(studyData) { const moveToButton = new qx.ui.menu.Button(this.tr("Move to..."), "@FontAwesome5Solid/folder/12"); moveToButton["moveToButton"] = true; @@ -1666,17 +1713,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const data = e.getData(); const destWorkspaceId = data["workspaceId"]; const destFolderId = data["folderId"]; - - if (destWorkspaceId === currentWorkspaceId) { - this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); - } else { - const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); - confirmationWin.addListener("close", () => { - if (confirmationWin.getConfirmed()) { - this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); - } - }, this); - } + this._moveStudyToFolderReqested(studyData, destWorkspaceId, destFolderId); }, this); moveStudyTo.addListener("cancel", () => win.close()); }, this); @@ -1695,11 +1732,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }; return osparc.data.Resources.fetch("studies", "moveToWorkspace", params) - .then(() => studyData["workspaceId"] = destWorkspaceId) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); + .then(() => studyData["workspaceId"] = destWorkspaceId); }, __moveStudyToFolder: function(studyData, destFolderId) { @@ -1717,15 +1750,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .then(() => studyData["folderId"] = destFolderId); }, - _studyToFolderRequested: function(data) { - const { - studyData, - destWorkspaceId, - destFolderId, - } = data; - this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); - }, - __getDuplicateMenuButton: function(studyData) { const duplicateButton = new qx.ui.menu.Button(this.tr("Duplicate"), "@FontAwesome5Solid/copy/12"); duplicateButton["duplicateButton"] = true; @@ -2050,7 +2074,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const confirmationWin = new osparc.ui.window.Confirmation(msg).set({ caption: this.tr("Move to Trash"), confirmText: this.tr("Move to Trash"), - confirmAction: "delete" + confirmAction: "warning", }); osparc.utils.Utils.setIdToWidget(confirmationWin.getConfirmButton(), "confirmDeleteStudyBtn"); return confirmationWin; 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 1d19d05f390..0df1dc1d8be 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 @@ -64,24 +64,17 @@ qx.Class.define("osparc.desktop.account.MyAccount", { authData.bind("username", usernameLabel, "value"); layout.add(usernameLabel); - const nameLabel = new qx.ui.basic.Label().set({ + const fullNameLabel = new qx.ui.basic.Label().set({ font: "text-13", alignX: "center" }); - layout.add(nameLabel); - const updateName = () => { - let name = ""; - if (authData.getFirstName()) { - name += authData.getFirstName(); - } - if (authData.getLastName()) { - name += " " + authData.getLastName(); - } - nameLabel.setValue(name); - } - updateName(); - authData.addListener("changeFirstName", updateName); - authData.addListener("changeLastName", updateName); + layout.add(fullNameLabel); + authData.bind("firstName", fullNameLabel, "value", { + converter: () => authData.getFullName() + }); + authData.bind("lastName", fullNameLabel, "value", { + converter: () => authData.getFullName() + }); if (authData.getRole() !== "user") { const role = authData.getFriendlyRole(); diff --git a/services/static-webserver/client/source/class/osparc/theme/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/Appearance.js index 7f6f8ccfe51..82b0c9b5666 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -19,6 +19,15 @@ qx.Theme.define("osparc.theme.Appearance", { extend: osparc.theme.common.Appearance, appearances: { + "strong-ui": { + style: () => { + return { + textColor: "default-button-text", + backgroundColor: "product-color", + }; + } + }, + "dragdrop-no-cursor": { style: () => { return { @@ -561,13 +570,12 @@ qx.Theme.define("osparc.theme.Appearance", { "widget/reset-button": {}, "form-button": { + include: "strong-ui", style: function(states) { const style = { decorator: "form-button", cursor: "pointer", - textColor: "default-button-text", padding: 5, - backgroundColor: "default-button" }; if (states.hovered) { style.decorator = "form-button-hovered"; @@ -953,11 +961,19 @@ qx.Theme.define("osparc.theme.Appearance", { } }, - // override in product "strong-button": { include: "form-button" }, + "warning-button": { + include: "form-button", + style: state => ({ + decorator: state.hovered || state.focused ? "form-button-warning-hover" : "form-button-warning", + backgroundColor: state.hovered || state.focused ? "default-button-hover-background" : "warning", + textColor: state.hovered || state.focused ? "default-button-text" : "black", + }) + }, + "danger-button": { include: "form-button", style: state => ({ diff --git a/services/static-webserver/client/source/class/osparc/theme/Decoration.js b/services/static-webserver/client/source/class/osparc/theme/Decoration.js index 7eae08b8f89..a1381421494 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Decoration.js +++ b/services/static-webserver/client/source/class/osparc/theme/Decoration.js @@ -554,6 +554,24 @@ qx.Theme.define("osparc.theme.Decoration", { } }, + // Warning button + "form-button-warning": { + include:"form-button-outlined", + style: { + color: "warning", + width: 1, + style: "solid" + } + }, + "form-button-warning-hover": { + include:"form-button-outlined", + style: { + color: "warning", + width: 1, + style: "solid" + } + }, + // Delete button "form-button-danger": { include:"form-button-outlined", diff --git a/services/static-webserver/client/source/class/osparc/theme/products/osparc/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/products/osparc/Appearance.js deleted file mode 100644 index 4aabe3dfec2..00000000000 --- a/services/static-webserver/client/source/class/osparc/theme/products/osparc/Appearance.js +++ /dev/null @@ -1,26 +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: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Theme.define("osparc.theme.products.osparc.Appearance", { - extend: osparc.theme.Appearance, - - appearances: { - "strong-button": { - include: "form-button" - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeDark.js b/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeDark.js index 896d57c93ad..d9fa660e1fc 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeDark.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeDark.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.osparc.ThemeDark", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.osparc.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeLight.js b/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeLight.js index 1db9fae0543..8b145f000a9 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeLight.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/osparc/ThemeLight.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.osparc.ThemeLight", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.osparc.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/s4l/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/products/s4l/Appearance.js deleted file mode 100644 index fe74eb92aa3..00000000000 --- a/services/static-webserver/client/source/class/osparc/theme/products/s4l/Appearance.js +++ /dev/null @@ -1,26 +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: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Theme.define("osparc.theme.products.s4l.Appearance", { - extend: osparc.theme.Appearance, - - appearances: { - "strong-button": { - include: "form-button" - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeDark.js b/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeDark.js index 50364a0ee31..8f067a59936 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeDark.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeDark.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.s4l.ThemeDark", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.s4l.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeLight.js b/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeLight.js index 08c99c6d382..6988ca53103 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeLight.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/s4l/ThemeLight.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.s4l.ThemeLight", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.s4l.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/tis/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/products/tis/Appearance.js deleted file mode 100644 index b259a72698a..00000000000 --- a/services/static-webserver/client/source/class/osparc/theme/products/tis/Appearance.js +++ /dev/null @@ -1,26 +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: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Theme.define("osparc.theme.products.tis.Appearance", { - extend: osparc.theme.Appearance, - - appearances: { - "strong-button": { - include: "form-button" - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeDark.js b/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeDark.js index 438a284d651..439239fd325 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeDark.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeDark.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.tis.ThemeDark", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.tis.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeLight.js b/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeLight.js index 10dc3c9a079..f4cb4252cf2 100644 --- a/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeLight.js +++ b/services/static-webserver/client/source/class/osparc/theme/products/tis/ThemeLight.js @@ -21,6 +21,6 @@ qx.Theme.define("osparc.theme.products.tis.ThemeLight", { decoration: osparc.theme.Decoration, font: osparc.theme.Font, icon: qx.theme.icon.Oxygen, - appearance: osparc.theme.products.tis.Appearance + appearance: osparc.theme.Appearance, } }); diff --git a/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js b/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js index 0fe65ef4f74..f8e42362405 100644 --- a/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js +++ b/services/static-webserver/client/source/class/osparc/ui/window/Confirmation.js @@ -47,7 +47,7 @@ qx.Class.define("osparc.ui.window.Confirmation", { }, confirmAction: { - check: [null, "create", "delete"], + check: [null, "create", "warning", "delete"], init: null, nullable: true, apply: "__applyConfirmAppearance" @@ -74,7 +74,10 @@ qx.Class.define("osparc.ui.window.Confirmation", { const confBtn = this.__confirmButton; switch (confirmationAction) { case "create": - confBtn.setAppearance("form-button"); + confBtn.setAppearance("strong-button"); + break; + case "warning": + confBtn.setAppearance("warning-button"); break; case "delete": confBtn.setAppearance("danger-button"); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js index 75b33a3229d..ea3897841fa 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js @@ -163,7 +163,10 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelListItem", { __applyPurchases: function(purchases) { if (purchases.length) { - this.setBackgroundColor("strong-main"); + this.set({ + textColor: "default-button-text", + backgroundColor: "strong-main", + }) } }, 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 dbffeefed8e..dd4f9567f4d 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js @@ -22,7 +22,8 @@ qx.Class.define("osparc.vipMarket.Market", { this.base(arguments); const miniWallet = osparc.desktop.credits.BillingCenter.createMiniWalletView().set({ - paddingRight: 10 + paddingRight: 10, + minWidth: 150, }); this.addWidgetOnTopOfTheTabs(miniWallet); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js index a7adecbf386..79b2626f260 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js @@ -126,7 +126,8 @@ qx.Class.define("osparc.vipMarket.VipMarket", { decorator: "no-border", spacing: 5, minWidth: 250, - maxWidth: 250 + maxWidth: 250, + backgroundColor: "transparent", }); this.getChildControl("left-side").add(control, { flex: 1