diff --git a/index.html b/index.html index 9723102fbf222a41d7425880bad64e75952b7018..a951d42ae4019ff6157a2effe93d4b14430324b7 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ <meta charset="UTF-8"/> <link rel="icon" href="/favicon.ico"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> - <title>CloWM - Cloud-based Workflow Manager</title> + <title>CloWM</title> <meta name="description" content="The Cloud-based Workflow Manager (CloWM) is a service hosted at Bielefeld University that can transform your Nextflow workflow into a public webservice and provide compute and storage resources for executing your registered workflow"/> <script src="/env.js"></script> </head> diff --git a/src/components/DraggableLists.vue b/src/components/DraggableLists.vue index bb72d1d5aad4368b36398d1d755239c9a42f4472..6f44ea1188f698d110568cc0a4cf7aaf65494d59 100644 --- a/src/components/DraggableLists.vue +++ b/src/components/DraggableLists.vue @@ -51,7 +51,7 @@ onMounted(() => { <h5><slot name="leftHeader" /></h5> <ul id="items" - class="list-group flex-fill border border-dashed p-1 overflow-y-scroll" + class="list-group flex-fill border border-dashed p-1 overflow-y-auto" ref="leftListElement" style="max-height: 40vh" > @@ -72,7 +72,7 @@ onMounted(() => { <h5><slot name="rightHeader" /></h5> <ul id="items" - class="list-group flex-fill border border-dashed p-1 overflow-y-scroll" + class="list-group flex-fill border border-dashed p-1 overflow-y-auto" ref="rightListElement" style="max-height: 40vh" > diff --git a/src/components/modals/BootstrapModal.vue b/src/components/modals/BootstrapModal.vue index cf6664ba3194bd13886f557cf8df54ca29f994d9..23a086606f5313173e8205510595bd6221b9a764 100644 --- a/src/components/modals/BootstrapModal.vue +++ b/src/components/modals/BootstrapModal.vue @@ -6,6 +6,7 @@ const props = defineProps<{ modalLabel: string; staticBackdrop?: boolean; sizeModifier?: sizeModifierType; // https://getbootstrap.com/docs/5.3/components/modal/#optional-sizes, e.g. sm, lg and xl + trackModalValue?: string; }>(); type sizeModifierType = "sm" | "lg" | "xl"; diff --git a/src/components/modals/DeleteModal.vue b/src/components/modals/DeleteModal.vue index 0a7c662f06a0e2e73e43cf3d81b8719d846c54ed..a08260ee59f021ba7799d313e801db97704c245b 100644 --- a/src/components/modals/DeleteModal.vue +++ b/src/components/modals/DeleteModal.vue @@ -4,7 +4,7 @@ import { Modal } from "bootstrap"; import BootstrapModal from "@/components/modals/BootstrapModal.vue"; const props = defineProps<{ - modalID: string; + modalId: string; objectNameDelete?: string; backModalId?: string; }>(); @@ -28,13 +28,13 @@ function deleteObject() { } onMounted(() => { - deleteModal = Modal.getOrCreateInstance("#" + props.modalID); + deleteModal = Modal.getOrCreateInstance("#" + props.modalId); }); </script> <template> <bootstrap-modal - :modalId="props.modalID" + :modalId="props.modalId" :static-backdrop="true" modal-label="Confirm Delete Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue index 5e00b40d9e194a25a2df19f131b38a92c5f3aa96..ddb729e1c3110db94984597815db294e389ed67b 100644 --- a/src/components/object-storage/BucketListItem.vue +++ b/src/components/object-storage/BucketListItem.vue @@ -58,7 +58,7 @@ onMounted(() => { <template> <permission-modal v-if="permission != undefined && props.active" - :modalID="'view-permission-modal' + randomIDSuffix" + :modalId="'view-permission-modal' + randomIDSuffix" :bucket-name="props.bucket.name" :sub-folders="subFolder" :edit-user-permission="permission" @@ -70,7 +70,7 @@ onMounted(() => { /> <bucket-detail-modal v-if="props.active" - :modalID="'view-bucket-details-modal' + randomIDSuffix" + :modalId="'view-bucket-details-modal' + randomIDSuffix" :bucket="props.bucket" :edit-user-permission="permission" /> diff --git a/src/components/object-storage/modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue index 3ceebc6ed78d77aee57dcb89986b87c124abadf2..503b2c0216602aba446f49cb3b029edc71b18ee2 100644 --- a/src/components/object-storage/modals/BucketDetailModal.vue +++ b/src/components/object-storage/modals/BucketDetailModal.vue @@ -5,17 +5,18 @@ import dayjs from "dayjs"; import { filesize } from "filesize"; const props = defineProps<{ - modalID: string; + modalId: string; bucket: BucketOut; }>(); </script> <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="false" modal-label="Bucket Detail Modal" size-modifier="lg" + :track-modal-value="bucket.name" > <template v-slot:header> <h4> diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue index 68f8b3d1a386ca24b0b712836b728e7eeaec85d1..ca3b28db7f9b6e8e48c11f8c6564af5d7a1b99a0 100644 --- a/src/components/object-storage/modals/CopyObjectModal.vue +++ b/src/components/object-storage/modals/CopyObjectModal.vue @@ -10,7 +10,7 @@ import BootstrapToast from "@/components/BootstrapToast.vue"; const objectRepository = useS3ObjectStore(); const props = defineProps<{ - modalID: string; + modalId: string; srcObject: S3Object; srcBucket: string; }>(); @@ -77,7 +77,7 @@ watch( ); onMounted(() => { - copyModal = new Modal("#" + props.modalID); + copyModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); errorToast = new Toast("#errorToast-" + randomIDSuffix); }); @@ -95,10 +95,11 @@ onMounted(() => { Try again later </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Copy Object Modal" v-on="{ 'hidden.bs.modal': modalClosed }" + :track-modal-value="srcBucket + '/' + srcObject.Key" > <template v-slot:header> <h4>Copy file {{ getFileName(props.srcObject.Key) }}</h4> diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue index 9de88c11559748cd7fc22d701c42ef294b879af9..c2044fbad0a419e7f57d1babbf315dd45acec07c 100644 --- a/src/components/object-storage/modals/CreateBucketModal.vue +++ b/src/components/object-storage/modals/CreateBucketModal.vue @@ -25,13 +25,13 @@ const formState = reactive<{ }); const props = defineProps<{ - modalID: string; + modalId: string; }>(); let createBucketModal: Modal | null = null; onMounted(() => { - createBucketModal = new Modal("#" + props.modalID); + createBucketModal = new Modal("#" + props.modalId); }); function createBucket() { @@ -82,7 +82,7 @@ function modalClosed() { <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Create Bucket Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue index 7791c9c7a62de44f7b7e290a7be4d0b2fbd9c76d..e0b5b398f054829eb347c9a7033eb7b41660ed9b 100644 --- a/src/components/object-storage/modals/CreateFolderModal.vue +++ b/src/components/object-storage/modals/CreateFolderModal.vue @@ -9,7 +9,7 @@ import BootstrapToast from "@/components/BootstrapToast.vue"; const objectRepository = useS3ObjectStore(); const props = defineProps<{ - modalID: string; + modalId: string; bucketName: string; keyPrefix: string; }>(); @@ -61,7 +61,7 @@ function uploadFolder() { } onMounted(() => { - uploadModal = new Modal("#" + props.modalID); + uploadModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); errorToast = new Toast("#errorToast-" + randomIDSuffix); }); @@ -79,9 +79,12 @@ onMounted(() => { Try again later </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Create Folder Modal" + :track-modal-value=" + bucketName + (keyPrefix.length > 0 ? '/' + keyPrefix : '') + " > <template v-slot:header> <h4>Create folder in</h4> diff --git a/src/components/object-storage/modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue index c1c2eaf0b1f2006db22acb8f893daf221af573c8..f980229223b5f7c28871716e7e2e24c46673b0cf 100644 --- a/src/components/object-storage/modals/ObjectDetailModal.vue +++ b/src/components/object-storage/modals/ObjectDetailModal.vue @@ -8,7 +8,7 @@ import { useS3ObjectStore } from "@/stores/s3objects"; const objectRepository = useS3ObjectStore(); const props = defineProps<{ - modalID: string; + modalId: string; objectKey: string | undefined; bucket: string; }>(); @@ -53,9 +53,10 @@ onMounted(() => { <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="false" modal-label="Object Detail Modal" + :track-modal-value="bucket + '/' + objectKey" > <template v-slot:header> <h4>File Details</h4> diff --git a/src/components/object-storage/modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue index 9876e0d3dcb2d3bad224881b7d192d7751e98225..f2a08972f0b05acf3ab2bf374cbfd31e76536037 100644 --- a/src/components/object-storage/modals/PermissionListModal.vue +++ b/src/components/object-storage/modals/PermissionListModal.vue @@ -18,7 +18,7 @@ const userRepository = useAuthStore(); const props = defineProps<{ bucketName: string; subFolders: FolderTree; - modalID: string; + modalId: string; }>(); // Reactive State @@ -70,13 +70,14 @@ onBeforeMount(() => { :edit-user-permission="state.currentPermission" :bucket-name="state.currentPermission.bucket_name" :sub-folders="props.subFolders" - :back-modal-id="props.modalID" - :modalID="'permission-list-edit-modal' + randomIDSuffix" + :back-modal-id="props.modalId" + :modalId="'permission-list-edit-modal' + randomIDSuffix" /> <bootstrap-modal - :modalId="props.modalID" + :modalId="props.modalId" :static-backdrop="true" modal-label="Permission List Modal" + :track-modal-value="bucketName" > <template v-slot:header> Bucket Permissions for Bucket <i>{{ props.bucketName }}</i> diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue index 846aad4522e2f640ef46e01b4f6eec3086a798df..74a5e2f10f041af297ed5d94bd1e50e715ce18b0 100644 --- a/src/components/object-storage/modals/PermissionModal.vue +++ b/src/components/object-storage/modals/PermissionModal.vue @@ -22,7 +22,7 @@ import { useNameStore } from "@/stores/names"; // Props // ----------------------------------------------------------------------------- const props = defineProps<{ - modalID: string; + modalId: string; bucketName: string; subFolders: FolderTree; editUserPermission?: BucketPermissionOut; @@ -237,7 +237,7 @@ function updateUser(user: User) { // Lifecycle Hooks // ----------------------------------------------------------------------------- onMounted(() => { - permissionModal = new Modal("#" + props.modalID); + permissionModal = new Modal("#" + props.modalId); successToast = new Toast("#" + "toast-" + randomIDSuffix); updateLocalPermission(); }); @@ -256,19 +256,20 @@ function toTimestampChanged(target?: HTMLInputElement | null) { </script> <template> - <DeleteModal - :modalID="'delete-permission-modal' + randomIDSuffix" + <delete-modal + :modal-id="'delete-permission-modal' + randomIDSuffix" object-name-delete="permission" - :back-modal-id="modalID" + :back-modal-id="modalId" @confirm-delete=" confirmedDeletePermission(permission.bucket_name, permission.uid) " /> <search-user-modal :modal-id="'search-user-modal' + randomIDSuffix" - :back-modal-id="modalID" + :back-modal-id="modalId" filter-user-self @user-found="updateUser" + :track-modal-value="bucketName" /> <bootstrap-toast :toast-id="'toast-' + randomIDSuffix" @@ -281,7 +282,7 @@ function toTimestampChanged(target?: HTMLInputElement | null) { Permission </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Permission Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue index f317f780615a2dd905f952431514ba75af8fe0e4..eca85fd56cb1927ac00566be0f56f49f23fcd7af 100644 --- a/src/components/object-storage/modals/UploadObjectModal.vue +++ b/src/components/object-storage/modals/UploadObjectModal.vue @@ -10,7 +10,7 @@ const fsize = partial({ base: 2, standard: "jedec" }); const objectRepository = useS3ObjectStore(); const props = defineProps<{ - modalID: string; + modalId: string; bucketName: string; keyPrefix: string; editObjectFileName?: string; @@ -95,7 +95,7 @@ function fileChange() { } onMounted(() => { - uploadModal = new Modal("#" + props.modalID); + uploadModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); errorToast = new Toast("#errorToast-" + randomIDSuffix); }); @@ -113,9 +113,12 @@ onMounted(() => { Try again later </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modal-id="modalId" :static-backdrop="true" modal-label="Upload Object Modal" + :track-modal-value=" + bucketName + (keyPrefix.length > 0 ? '/' + keyPrefix : '') + " > <template #header> <h4>Upload file to</h4> diff --git a/src/components/resources/modals/CreateResourceModal.vue b/src/components/resources/modals/CreateResourceModal.vue index 8bf7a012971ac41ea5a94865e3018333f417986c..cdb044b8b4f490ef8cde4e4393c3975db043214a 100644 --- a/src/components/resources/modals/CreateResourceModal.vue +++ b/src/components/resources/modals/CreateResourceModal.vue @@ -30,7 +30,7 @@ const formState = reactive<{ }); const props = defineProps<{ - modalID: string; + modalId: string; }>(); let createResourceModal: Modal | null = null; @@ -79,7 +79,7 @@ function modalClosed() { } onMounted(() => { - createResourceModal = new Modal("#" + props.modalID); + createResourceModal = new Modal("#" + props.modalId); new Tooltip("#tooltip-new-resource-source"); new Tooltip("#tooltip-new-resource-release"); }); @@ -87,7 +87,7 @@ onMounted(() => { <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" static-backdrop modal-label="Create Resource Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/resources/modals/UpdateResourceModal.vue b/src/components/resources/modals/UpdateResourceModal.vue index f0e73c341af5384ae0b64216b26eb02a86e782c9..3a430a639f46415a8e164b16cc47aea6d91c547d 100644 --- a/src/components/resources/modals/UpdateResourceModal.vue +++ b/src/components/resources/modals/UpdateResourceModal.vue @@ -64,6 +64,7 @@ onMounted(() => { static-backdrop modal-label="Update Resource Modal" v-on="{ 'hidden.bs.modal': modalClosed }" + :track-modal-value="resource.resource_id" > <template #header> Update Resource <b>{{ props.resource.name }}</b></template diff --git a/src/components/resources/modals/UploadResourceInfoModal.vue b/src/components/resources/modals/UploadResourceInfoModal.vue index 02f4d182eacce48ce8da6921414e1ea1d1b13964..f25e1799a9f0523825df6095e4cb265aa592dad1 100644 --- a/src/components/resources/modals/UploadResourceInfoModal.vue +++ b/src/components/resources/modals/UploadResourceInfoModal.vue @@ -69,7 +69,7 @@ export S3_ENDPOINT_URL="${environment.S3_URL}" s5cmd cp --show-progress /PATH/TO/RESOURCE \\ ${resourceS3Path.value}`; } else if (activeTool.value === Tool.MINIO) { - return `mc alias set ${environment.S3_URL} "${s3Key.value.access_key}" "${s3Key.value.secret_key}" + return `mc alias set clowm-s3 ${environment.S3_URL} "${s3Key.value.access_key}" "${s3Key.value.secret_key}" mc cp /PATH/TO/RESOURCE \\ clowm-s3/clowm-resources/CLDB-eaa7aae4/eaaa1c99b14911ee9f5d0242ac120004/resource.tar.gz`; } else if (activeTool.value === Tool.PYTHON) { @@ -137,6 +137,7 @@ onMounted(() => { :modalId="props.modalId" modal-label="Upload Resource Info Modal" sizeModifier="lg" + :track-modal-value="resourceVersion?.resource_version_id" > <template #header>How to upload a resource to the cluster</template> <template #body> @@ -245,7 +246,7 @@ onMounted(() => { <pre class="w-100" ><code class="hljs language-bash">mc <span class="hljs-built_in">alias</span> <span - class="hljs-built_in">set</span> {{ environment.S3_URL }} <span + class="hljs-built_in">set</span> clowm-s3 {{ environment.S3_URL }} <span class="hljs-string">"{{ s3Key.access_key }}"</span> <span class="hljs-string">"{{ s3Key.secret_key }}"</span> mc <span class="hljs-built_in">cp</span> /PATH/TO/RESOURCE \ @@ -285,6 +286,17 @@ s3 = boto3.resource( key=<span class="hljs-string">"{{ resourceKey }}"</span> ).upload_fileobj(f)</code></pre> </template> + <b v-if="resourceVersion == undefined" class="text-danger"> + <font-awesome-icon + icon="fa-solid fa-triangle-exclamation" + class="me-1" + /> + Important: This is just example code + <font-awesome-icon + icon="fa-solid fa-triangle-exclamation" + class="ms-1" + /> + </b> </template> </li> <li> @@ -293,7 +305,7 @@ s3 = boto3.resource( Click <b>Request Synchronization</b> to request the synchronization to CloWM's compute cluster. </p> - <div class="btn-group" role="group" v-if="props.resourceVersion"> + <div class="btn-group mb-2" role="group" v-if="props.resourceVersion"> <button type="button" class="btn btn-primary" diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue index e0a960a263aab9a7839bc3734d99a2ac7b03c018..a9a63de654cb47a4725f47979cabfe8d23516e23 100644 --- a/src/components/workflows/WorkflowWithVersionsCard.vue +++ b/src/components/workflows/WorkflowWithVersionsCard.vue @@ -170,7 +170,7 @@ onMounted(() => { }, }" class="btn btn-primary dropdown-item" - >Add Metadata + >Add Parameter Metadata </router-link> </li> <li> diff --git a/src/components/workflows/modals/ArbitraryWorkflowModal.vue b/src/components/workflows/modals/ArbitraryWorkflowModal.vue index c33d00d11f856eeb47d9f0dba00045f31215723a..9d2a0ec88180641f0cf9f3b33565c69c707bc215 100644 --- a/src/components/workflows/modals/ArbitraryWorkflowModal.vue +++ b/src/components/workflows/modals/ArbitraryWorkflowModal.vue @@ -13,7 +13,7 @@ import { useWorkflowStore } from "@/stores/workflows"; import type { WorkflowModeOut } from "@/client/workflow"; const props = defineProps<{ - modalID: string; + modalId: string; }>(); let createWorkflowModal: Modal | null = null; @@ -171,7 +171,7 @@ const gitIcon = computed<string>(() => ); onMounted(() => { - createWorkflowModal = new Modal("#" + props.modalID); + createWorkflowModal = new Modal("#" + props.modalId); privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", { toggle: false, }); @@ -186,7 +186,7 @@ onMounted(() => { <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="false" modal-label="Create Workflow Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue index d82776236d2b09dec87b4fe60f1794439339629b..a75376e85af34975e10d0c60de3cc7c61fb7e85c 100644 --- a/src/components/workflows/modals/CreateWorkflowModal.vue +++ b/src/components/workflows/modals/CreateWorkflowModal.vue @@ -29,7 +29,7 @@ const emit = defineEmits<{ // Props // ============================================================================= const props = defineProps<{ - modalID: string; + modalId: string; }>(); // Bootstrap Elements @@ -302,7 +302,7 @@ function removeMode(index: number) { // Lifecycle Events // ============================================================================= onMounted(() => { - createWorkflowModal = new Modal("#" + props.modalID); + createWorkflowModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); privateRepositoryCollapse = new Collapse("#privateRepositoryCollapse", { toggle: false, @@ -324,7 +324,7 @@ onMounted(() => { Successfully created Workflow </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Create Workflow Modal" v-on="{ 'hidden.bs.modal': modalClosed }" diff --git a/src/components/workflows/modals/ParameterModal.vue b/src/components/workflows/modals/ParameterModal.vue index 5ae588a8cbbde06415c5ae967bd8eeb0f9d533e3..5b7d852eda2546c4390295f1f9818c3682ac5a29 100644 --- a/src/components/workflows/modals/ParameterModal.vue +++ b/src/components/workflows/modals/ParameterModal.vue @@ -20,7 +20,7 @@ const router = useRouter(); let parameterModal: Modal | null = null; const props = defineProps<{ - modalID: string; + modalId: string; executionId?: string; }>(); @@ -168,16 +168,17 @@ watch( onMounted(() => { fetchWorkflowExecutionParameters(props.executionId); - parameterModal = Modal.getOrCreateInstance("#" + props.modalID); + parameterModal = Modal.getOrCreateInstance("#" + props.modalId); }); </script> <template> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="false" modal-label="Workflow Execution Parameters Modal" size-modifier="lg" + :track-modal-value="executionId" > <template v-slot:header >Workflow Execution Parameters diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue index 43f6a6129bdd87084aee581def2f23a99bf85636..1b9bf27bb49c2bc31d7dd467aeb2980665ec2397 100644 --- a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue @@ -27,7 +27,7 @@ const credentialsInputElement = ref<HTMLInputElement | undefined>(undefined); // Props // ============================================================================= const props = defineProps<{ - modalID: string; + modalId: string; workflow: WorkflowOut; }>(); @@ -119,7 +119,7 @@ function deleteCredentials() { // Lifecycle Events // ============================================================================= onMounted(() => { - updateCredentialsModal = new Modal("#" + props.modalID); + updateCredentialsModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); }); </script> @@ -127,20 +127,21 @@ onMounted(() => { <template> <DeleteModal v-if="props.workflow.private" - :modalID="'delete-credentials-modal' + randomIDSuffix" + :modalId="'delete-credentials-modal' + randomIDSuffix" :object-name-delete="'credentials for workflow ' + props.workflow.name" - :back-modal-id="modalID" + :back-modal-id="modalId" @confirm-delete="deleteCredentials()" /> <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> Successfully updated credentials </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Update Workflow Version Icon Modal" size-modifier="lg" v-on="{ 'hidden.bs.modal': modalClosed }" + :track-modal-value="workflow.workflow_id" > <template #header> Update git repository credentials for Workflow diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue index e47ea0c79939bbfa43e58c5e984e3816d79473aa..b69487c2725d96e22eaca180582eeb324541352c 100644 --- a/src/components/workflows/modals/UpdateWorkflowModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowModal.vue @@ -47,7 +47,7 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8); // Props // ============================================================================= const props = defineProps<{ - modalID: string; + modalId: string; workflow: WorkflowOut; }>(); @@ -299,7 +299,7 @@ function removeOldMode(mode_id: string) { // Lifecycle Events // ============================================================================= onMounted(() => { - updateWorkflowModal = new Modal("#" + props.modalID); + updateWorkflowModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); workflowModesCollapse = new Collapse("#updateWorkflowModesCollapse", { toggle: false, @@ -312,11 +312,12 @@ onMounted(() => { Successfully updated Workflow </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" modal-label="Update Workflow Modal" v-on="{ 'hidden.bs.modal': modalClosed }" size-modifier="lg" + :track-modal-value="workflow.workflow_id" > <template #header> Update Workflow diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue index 9f529d0c080f4c591d33162dab4953a449596031..1b18fd83a40744e058061ae66749e1b975d69ded 100644 --- a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue @@ -27,7 +27,7 @@ const iconInputElement = ref<HTMLInputElement | undefined>(undefined); // Props // ============================================================================= const props = defineProps<{ - modalID: string; + modalId: string; version: WorkflowVersion; workflowName?: string; }>(); @@ -131,7 +131,7 @@ function deleteIcon() { // Lifecycle Events // ============================================================================= onMounted(() => { - updateIconModal = new Modal("#" + props.modalID); + updateIconModal = new Modal("#" + props.modalId); successToast = new Toast("#successToast-" + randomIDSuffix); }); </script> @@ -139,9 +139,9 @@ onMounted(() => { <template> <DeleteModal v-if="props.version.icon_url" - :modalID="'delete-icon-modal' + randomIDSuffix" + :modalId="'delete-icon-modal' + randomIDSuffix" :object-name-delete="'icon for workflow ' + props.workflowName" - :back-modal-id="modalID" + :back-modal-id="modalId" @confirm-delete="deleteIcon()" /> <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> @@ -149,10 +149,11 @@ onMounted(() => { <template v-else>Successfully deleted icon</template> </bootstrap-toast> <bootstrap-modal - :modalId="modalID" + :modalId="modalId" :static-backdrop="true" - modal-label="Update Workflow Version IconModal" + modal-label="Update Workflow Version Icon Modal" v-on="{ 'hidden.bs.modal': modalClosed }" + :track-modal-value="version.workflow_version_id" > <template #header> Update Icon Workflow diff --git a/src/router/adminRoutes.ts b/src/router/adminRoutes.ts index e146270b70d3204f470c139a12c7299becaed214..4015a57c61f9d5c88abd7ae65b8ea8b35487b20b 100644 --- a/src/router/adminRoutes.ts +++ b/src/router/adminRoutes.ts @@ -7,6 +7,7 @@ export const adminRoutes: RouteRecordRaw[] = [ component: () => import("../views/admin/AdminResourcesView.vue"), meta: { requiresAdminRole: true, + title: "Manage Resource", }, }, { @@ -15,6 +16,7 @@ export const adminRoutes: RouteRecordRaw[] = [ component: () => import("../views/admin/AdminUsersView.vue"), meta: { requiresAdminRole: true, + title: "Manage Users", }, }, ]; diff --git a/src/router/index.ts b/src/router/index.ts index 925aee91ea87a48738dd3fba38f8b475a9f45566..71b75010707d4dac8bf39623287370e942b58385 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -24,6 +24,9 @@ const router = createRouter({ path: "/login", name: "login", component: LoginView, + meta: { + title: "Login", + }, props: (route) => ({ returnPath: route.query.return_path ?? undefined, loginError: route.query.login_error ?? undefined, @@ -37,8 +40,18 @@ const router = createRouter({ }, { path: "/:pathMatch(.*)", + meta: { + title: "Error", + }, component: () => import("../views/NotFoundView.vue"), }, ], }); + +router.afterEach((to) => { + if (to.meta?.title) { + document.title = to.meta.title + " - CloWM"; + } +}); + export default router; diff --git a/src/router/resourceRoutes.ts b/src/router/resourceRoutes.ts index 69cd0cffc0282ff9dc6063bc6bd47e551bd50178..4dd57177d3222e77e3541c2cb387b79da3d3b654 100644 --- a/src/router/resourceRoutes.ts +++ b/src/router/resourceRoutes.ts @@ -5,6 +5,9 @@ export const resourceRoutes: RouteRecordRaw[] = [ path: "resources", name: "resources", component: () => import("../views/resources/ListResourcesView.vue"), + meta: { + title: "Resource", + }, }, { path: "maintainer/resources", @@ -12,6 +15,7 @@ export const resourceRoutes: RouteRecordRaw[] = [ component: () => import("../views/resources/MyResourcesView.vue"), meta: { requiresMaintainerRole: true, + title: "My Resources", }, }, { @@ -20,6 +24,7 @@ export const resourceRoutes: RouteRecordRaw[] = [ component: () => import("../views/resources/ReviewResourceView.vue"), meta: { requiresReviewerRole: true, + title: "Review Resources", }, }, ]; diff --git a/src/router/s3Routes.ts b/src/router/s3Routes.ts index 30ff3e0799e723c28e0c9dc61d78ea3b503680cb..bc7c73b29c5dc703d284e2bcd21a6b0c964adac9 100644 --- a/src/router/s3Routes.ts +++ b/src/router/s3Routes.ts @@ -8,6 +8,9 @@ export const s3Routes: RouteRecordRaw[] = [ props: (route) => ({ bucketName: route.params.bucketName ?? undefined, }), + meta: { + title: "Buckets", + }, children: [ { path: ":bucketName/:subFolders*", @@ -20,6 +23,9 @@ export const s3Routes: RouteRecordRaw[] = [ { path: "object-storage/s3-keys", name: "s3_keys", + meta: { + title: "S3 Keys", + }, component: () => import("../views/object-storage/S3KeysView.vue"), }, ]; diff --git a/src/router/workflowRoutes.ts b/src/router/workflowRoutes.ts index 4441fcb4a6ef47ee04fed778cbdc735c2df7764a..14284c19f05c3a59b31054c83e02c10e5152f87a 100644 --- a/src/router/workflowRoutes.ts +++ b/src/router/workflowRoutes.ts @@ -4,12 +4,18 @@ export const workflowRoutes: RouteRecordRaw[] = [ { path: "workflow-executions", name: "workflow-executions", + meta: { + title: "My Workflow Executions", + }, component: () => import("../views/workflows/ListWorkflowExecutionsView.vue"), }, { path: "workflows", name: "workflows", + meta: { + title: "Workflows", + }, component: () => import("../views/workflows/ListWorkflowsView.vue"), }, { @@ -18,6 +24,7 @@ export const workflowRoutes: RouteRecordRaw[] = [ component: () => import("../views/workflows/MyWorkflowsView.vue"), meta: { requiresDeveloperRole: true, + title: "My Workflows", }, }, { @@ -30,6 +37,7 @@ export const workflowRoutes: RouteRecordRaw[] = [ }), meta: { requiresDeveloperRole: true, + title: "clowm_info.json", }, }, { @@ -38,6 +46,7 @@ export const workflowRoutes: RouteRecordRaw[] = [ component: () => import("../views/workflows/ReviewWorkflowsView.vue"), meta: { requiresReviewerRole: true, + title: "Review Workflows", }, }, { @@ -46,6 +55,7 @@ export const workflowRoutes: RouteRecordRaw[] = [ component: () => import("../views/workflows/ArbitraryWorkflowView.vue"), meta: { requiresDeveloperRole: true, + title: "Arbitrary Workflow", }, props: (route) => ({ wid: route.query.wid, diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue index fbd233e1e3407e53b68e7e2fe28565074acf4d32..0608ca0c4d2623a2eb3369637d6155d11520029c 100644 --- a/src/views/object-storage/BucketView.vue +++ b/src/views/object-storage/BucketView.vue @@ -229,6 +229,7 @@ watch( () => props.bucketName, (newBucketName, oldBucketName) => { if (oldBucketName !== newBucketName) { + document.title = newBucketName + " - CloWM"; objectState.viewDetailKey = undefined; // If bucket is changed, update the objects objectState.bucketPermissionError = false; @@ -425,7 +426,7 @@ function getObjectFileName(key: string): string { Successfully deleted {{ deleteObjectsState.deletedItem }} </bootstrap-toast> <DeleteModal - modalID="delete-object-modal" + modalId="delete-object-modal" :object-name-delete="deleteObjectsState.potentialObjectToDelete" :back-modal-id="undefined" @confirm-delete=" @@ -515,7 +516,7 @@ function getObjectFileName(key: string): string { </button> <upload-object-modal :bucket-name="props.bucketName" - modalID="upload-object-modal" + modalId="upload-object-modal" :key-prefix="currentSubFolders.join('/')" :edit-object-file-name="undefined" /> @@ -534,7 +535,7 @@ function getObjectFileName(key: string): string { </button> <create-folder-modal :bucket-name="props.bucketName" - modalID="create-folder-modal" + modalId="create-folder-modal" :key-prefix="currentSubFolders.join('/')" /> <!-- Add bucket permission button --> @@ -553,7 +554,7 @@ function getObjectFileName(key: string): string { </button> <permission-modal v-if="!authStore.foreignUser" - modalID="create-permission-modal" + modalId="create-permission-modal" :bucket-name="props.bucketName" :sub-folders="folderStructure" :edit-user-permission="undefined" @@ -583,7 +584,7 @@ function getObjectFileName(key: string): string { " :bucket-name="props.bucketName" :sub-folders="folderStructure" - modalID="permission-list-modal" + modalId="permission-list-modal" /> </div> </div> @@ -783,19 +784,19 @@ function getObjectFileName(key: string): string { </table> <upload-object-modal :bucket-name="props.bucketName" - modalID="edit-object-modal" + modalId="edit-object-modal" :key-prefix="currentSubFolders.join('/')" :edit-object-file-name="getObjectFileName(objectState.editObjectKey)" /> <copy-object-modal :src-object="objectState.copyObject" :src-bucket="bucketName" - modalID="copy-object-modal" + modalId="copy-object-modal" /> <object-detail-modal :bucket="bucketName" :object-key="objectState.viewDetailKey" - modalID="detail-object-modal" + modalId="detail-object-modal" /> </div> </div> diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue index 1797014c90c9e548ef3fef0bf58507d1552ac5f1..d47ff0905b8c53b979c281fdafd6523995db15f8 100644 --- a/src/views/object-storage/BucketsView.vue +++ b/src/views/object-storage/BucketsView.vue @@ -81,7 +81,7 @@ onMounted(() => { <template> <DeleteModal - modalID="delete-bucket-modal" + modalId="delete-bucket-modal" :object-name-delete="bucketsState.potentialDeleteBucketName" :back-modal-id="undefined" @confirm-delete=" @@ -89,7 +89,7 @@ onMounted(() => { " /> <CreateBucketModal - modalID="create-bucket-modal" + modalId="create-bucket-modal" v-if="!authStore.foreignUser" /> <div class="row border-bottom"> diff --git a/src/views/object-storage/S3KeyView.vue b/src/views/object-storage/S3KeyView.vue index 792a20097c486b07eabf925ed74ae0dba49d1e22..2f1b23cee9620d75d14911f908bae48b64e44a22 100644 --- a/src/views/object-storage/S3KeyView.vue +++ b/src/views/object-storage/S3KeyView.vue @@ -39,7 +39,7 @@ function deleteKeyTrigger() { <template> <delete-modal - modalID="delete-key-modal" + modalId="delete-key-modal" :object-name-delete="'S3 Key ' + props.s3key.access_key" @confirm-delete="deleteKeyTrigger" /> diff --git a/src/views/resources/MyResourcesView.vue b/src/views/resources/MyResourcesView.vue index 5b2b3652f4d3573d6038dd54f7e291af2e80c934..72aaf6be23fed41c6ff539143c69318cee8ba670 100644 --- a/src/views/resources/MyResourcesView.vue +++ b/src/views/resources/MyResourcesView.vue @@ -52,7 +52,7 @@ onMounted(() => { </script> <template> - <create-resource-modal modal-i-d="createResourceModal" /> + <create-resource-modal modal-id="createResourceModal" /> <upload-resource-info-modal modal-id="uploadResourceInfoModal" :resource-version="resourceState.resourceVersionInfo" diff --git a/src/views/workflows/CreateClowmInfoView.vue b/src/views/workflows/CreateClowmInfoView.vue index 8137d54631b55a798489e39194196d132c6049b5..4c4261f4edd08479d4407c5c4ca52c3035185e38 100644 --- a/src/views/workflows/CreateClowmInfoView.vue +++ b/src/views/workflows/CreateClowmInfoView.vue @@ -195,40 +195,30 @@ onMounted(() => { <template> <div class="row border-bottom mb-4"> - <h2 class="mb-2">Enhance your Workflow</h2> + <h2 class="mb-2"> + Add parameter metadata + <template v-if="props.workflow_id" + >to + {{ workflowRepository.workflowMapping[props.workflow_id]?.name }} + </template> + </h2> </div> <div> - Enhance your Workflow with CloWM: + Enhance your Workflow with CloWM specific parameter metadata to simplify the + usability and increase the user experience of your workflow within CloWM. + This can be achieved by simply adding a file named + <code>clowm_info.json</code>, containing parameter metadata, into your + repositories root directory. With the provided metadata a workflow developer + can provide <ul> - <li> - Maximize the potential of your workflow within CloWM by incorporating a - <code>clowm_info.json</code> file directly into your repository's root - directory. This simple addition empowers you to provide comprehensive - details about your workflow's parameters and overall structure, - enhancing its usability and user experience. - </li> - <li> - Customize Parameters: Infuse semantic meaning into various parameters, - enriching the understanding and functionality of your workflow. - </li> - <li> - Publication Linking: Seamlessly integrate a DOI link to your - publication, facilitating easy access to additional resources and - information. - </li> - <li> - Experience Enhanced Workflow: Elevate your workflow's presentation on - the CloWM platform by ensuring essential information is readily - available to users. - </li> + <li>semantic meanings by parameter designation,</li> + <li>example parameters to allow users a easy tryout of the workflow,</li> + <li>and DOI links to publications and additional information</li> </ul> + For guidance on creating file <code>clowm_info.json</code> please explore + tabs below. </div> - <p> - Unlock the full potential of your workflow in CloWM by exploring the tabs - below for guidance on creating and implementing the - <code>clowm_info.json</code> file. - </p> - <div class="accordion mb-4" id="clowmInfoAccordion"> + <div class="accordion my-4" id="clowmInfoAccordion"> <div class="accordion-item"> <h2 class="accordion-header"> <button @@ -239,7 +229,7 @@ onMounted(() => { aria-expanded="true" aria-controls="clowmInfoAccordion-input" > - Input Parameters + Designate input parameters </button> </h2> <div @@ -257,8 +247,8 @@ onMounted(() => { :left-list="parameterPools.input" :right-list="infoState.inputParameters" > - <template #leftHeader>Workflow Parameters</template> - <template #rightHeader>Input Parameters</template> + <template #leftHeader>Workflow parameters</template> + <template #rightHeader>Selected input parameters</template> </draggable-lists> </div> </div> @@ -273,7 +263,7 @@ onMounted(() => { aria-expanded="false" aria-controls="clowmInfoAccordion-output" > - Output Parameters + Designate output parameters </button> </h2> <div @@ -291,8 +281,8 @@ onMounted(() => { :left-list="parameterPools.output" :right-list="infoState.outputParameters" > - <template #leftHeader>Workflow Parameters</template> - <template #rightHeader>Output Parameters</template> + <template #leftHeader>Workflow parameters</template> + <template #rightHeader>Selected output parameters</template> </draggable-lists> </div> </div> @@ -307,7 +297,7 @@ onMounted(() => { aria-expanded="false" aria-controls="clowmInfoAccordion-resource" > - Resource Parameters + Designate resource parameters </button> </h2> <div @@ -335,8 +325,8 @@ onMounted(() => { :right-list="infoState.resourceParameters" v-else > - <template #leftHeader>Workflow Parameters</template> - <template #rightHeader>Resources Parameters</template> + <template #leftHeader>Workflow parameters</template> + <template #rightHeader>Selected resources parameters</template> </draggable-lists> </div> </div> @@ -351,7 +341,7 @@ onMounted(() => { aria-expanded="false" aria-controls="clowmInfoAccordion-example" > - Example Parameters + Set example parameters </button> </h2> <div @@ -361,20 +351,22 @@ onMounted(() => { > <div class="accordion-body"> <p> - <b>Enhance Workflow Testing</b>: Incorporate example parameters + <b>Easy trial of a workflow</b>: Incorporate example parameters using publicly available test data. Easily achieve this by including a sample dataset in your Git repository and linking its URL as the input parameter. </p> <p> - <b>Guidance on Parameter Setting</b>: Avoid specifying parameters - that define output locations or resource paths, as these are heavily - dependent on the user or execution environment. + Guidance on Parameter Setting: Avoid specifying parameters that + define output locations or resource paths, as these heavily depend + on the user or execution environment. </p> <div - class="d-flex flex-wrap overflow-y-scroll p-1 border rounded border-dashed mb-2" + class="d-flex flex-wrap overflow-y-auto p-1 border rounded border-dashed mb-2" style="max-height: 30vh" + v-if="parameterPools.examples.length > 0" > + <b class="ms-1 w-100">Workflow parameters:</b> <div class="w-fit border px-2 rounded cursor-pointer m-1 parameter-container" v-for="(param, index) in parameterPools.examples" @@ -499,9 +491,8 @@ onMounted(() => { > <div class="accordion-body"> <p> - <b>Share Publications Easily</b>: Include the DOI for your - workflow's related publications and watch as the generated links - seamlessly appear on your workflow's dedicated page. + <b>Document Object Identifier</b>: Add DOIs to your workflow. This + will be used to generate link on your workflows dedicated page. </p> <button type="button" class="btn btn-primary" @click="addDoi"> Add DOI diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue index 5b96f035d1d9ab73fb124e38e1aab7234aefdd8f..209e11ff5f9167ef145472bfedbf44f0d4a3fa99 100644 --- a/src/views/workflows/ListWorkflowExecutionsView.vue +++ b/src/views/workflows/ListWorkflowExecutionsView.vue @@ -154,14 +154,14 @@ onUnmounted(() => { <template> <delete-modal - modal-i-d="deleteWorkflowExecutionModal" + modal-id="deleteWorkflowExecutionModal" :object-name-delete="deleteModalString" @confirm-delete=" deleteWorkflowExecution(executionsState.executionToDelete?.execution_id) " /> <parameter-modal - modal-i-d="workflowExecutionParameterModal" + modal-id="workflowExecutionParameterModal" :execution-id="executionsState.executionParameters" /> <div diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue index 4c54cf1f25cee79d529e4d1696698efbd38ba6fc..07a77242c10fec64260cd9c57164c9e58f7c0c96 100644 --- a/src/views/workflows/ListWorkflowsView.vue +++ b/src/views/workflows/ListWorkflowsView.vue @@ -144,7 +144,7 @@ onMounted(() => { > Test my Workflow </button> - <arbitrary-workflow-modal modal-i-d="arbitraryWorkflowModal" /> + <arbitrary-workflow-modal modal-id="arbitraryWorkflowModal" /> </div> <div v-if="!workflowsState.loading"> <div diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue index 8b5b935a86a60c7e376617321e09d2e82cb1061b..89632a10560f296af879ec8cd8b2b6a430cc3c90 100644 --- a/src/views/workflows/MyWorkflowsView.vue +++ b/src/views/workflows/MyWorkflowsView.vue @@ -86,13 +86,13 @@ onMounted(() => { </script> <template> - <create-workflow-modal modal-i-d="createWorkflowModal" /> + <create-workflow-modal modal-id="createWorkflowModal" /> <update-workflow-modal :workflow="workflowsState.updateWorkflow" - modal-i-d="updateWorkflowModal" + modal-id="updateWorkflowModal" /> <delete-modal - modal-i-d="deleteWorkflowModal" + modal-id="deleteWorkflowModal" :object-name-delete="workflowsState.potentialWorkflowDelete?.name" @confirm-delete=" confirmedWorkflowDelete( @@ -101,7 +101,7 @@ onMounted(() => { " /> <update-workflow-version-icon-modal - modal-i-d="updateWorkflowVersionIconModal" + modal-id="updateWorkflowVersionIconModal" :workflow-name=" workflowRepository.comprehensiveWorkflowMapping[ workflowsState.updateIconVersion.workflow_id @@ -111,7 +111,7 @@ onMounted(() => { /> <update-workflow-credentials-modal :workflow="workflowsState.updateWorkflow" - modal-i-d="updateWorkflowCredentialsModal" + modal-id="updateWorkflowCredentialsModal" /> <div class="row border-bottom mb-4 justify-content-between align-items-center pb-2" diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue index 8ad2714a3795dbeb33aef3491fb18dbb5eeae61c..28d53d452fb1ae0198a62c32d87c138010a91233 100644 --- a/src/views/workflows/WorkflowView.vue +++ b/src/views/workflows/WorkflowView.vue @@ -133,6 +133,7 @@ function updateWorkflow(workflowId: string) { workflowState.initialOpen = false; }) .then((workflow) => { + document.title = workflow.name + " - CloWM"; if (props.versionId == undefined) { updateVersion(workflow.versions[0]?.workflow_version_id); }