From e28a3a9b0ac1c266c46d92a90bd63a555f25f8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Tue, 5 Mar 2024 15:43:47 +0100 Subject: [PATCH] Update resource info modal to new flow #100 --- src/client/workflow/models/WorkflowOut.ts | 2 +- src/components/CopyToClipboardIcon.vue | 6 +- .../resources/RequestReviewButton.vue | 44 +++++++++++++ src/components/resources/ResourceCard.vue | 38 ++++-------- .../{ => modals}/ResourceVersionInfoModal.vue | 0 .../modals/UploadResourceInfoModal.vue | 52 ++++++++-------- .../workflows/WorkflowWithVersionsCard.vue | 62 ++++++++++++------- src/views/admin/AdminResourcesView.vue | 2 +- src/views/admin/AdminSyncRequestsView.vue | 2 +- src/views/resources/ListResourcesView.vue | 14 ++--- src/views/resources/ReviewResourceView.vue | 2 +- src/views/workflows/MyWorkflowsView.vue | 2 +- src/views/workflows/ReviewWorkflowsView.vue | 8 +-- src/views/workflows/WorkflowView.vue | 9 +++ 14 files changed, 148 insertions(+), 95 deletions(-) create mode 100644 src/components/resources/RequestReviewButton.vue rename src/components/resources/{ => modals}/ResourceVersionInfoModal.vue (100%) diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts index 79bc468..90fbae3 100644 --- a/src/client/workflow/models/WorkflowOut.ts +++ b/src/client/workflow/models/WorkflowOut.ts @@ -27,7 +27,7 @@ export type WorkflowOut = { /** * ID of developer of the workflow */ - developer_id?: (string | null); + developer_id: string; /** * Flag if the workflow is hosted in a private git repository */ diff --git a/src/components/CopyToClipboardIcon.vue b/src/components/CopyToClipboardIcon.vue index 6e85078..6b9f65d 100644 --- a/src/components/CopyToClipboardIcon.vue +++ b/src/components/CopyToClipboardIcon.vue @@ -45,7 +45,11 @@ onMounted(() => { color-class="danger" >Can't copy to clipboard </bootstrap-toast> - <button v-if="props.button" @click="copyToClipboard" class="btn btn-primary"> + <button + v-if="props.button" + @click="copyToClipboard" + class="btn btn-primary btn-sm" + > Copy to Clipboard <font-awesome-icon icon="fa-solid fa-clipboard" class="ms-1" /> </button> diff --git a/src/components/resources/RequestReviewButton.vue b/src/components/resources/RequestReviewButton.vue new file mode 100644 index 0000000..fa7599d --- /dev/null +++ b/src/components/resources/RequestReviewButton.vue @@ -0,0 +1,44 @@ +<script setup lang="ts"> +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; +import { onMounted } from "vue"; +import { Tooltip } from "bootstrap"; + +const props = defineProps<{ + disabled?: boolean; +}>(); + +const emit = defineEmits<{ + (e: "click-review"): void; + (e: "click-refresh"): void; +}>(); +const randomIDSuffix = Math.random().toString(16).substring(2, 8); + +onMounted(() => { + new Tooltip("#refresh-s3-" + randomIDSuffix); +}); +</script> + +<template> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-primary" + :disabled="props.disabled" + @click="emit('click-review')" + > + Request Review + </button> + <button + :id="'refresh-s3-' + randomIDSuffix" + type="button" + class="btn btn-primary" + @click="emit('click-refresh')" + data-bs-toggle="tooltip" + data-bs-title="Check if uploaded resource is ready" + > + <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" /> + </button> + </div> +</template> + +<style scoped></style> diff --git a/src/components/resources/ResourceCard.vue b/src/components/resources/ResourceCard.vue index ddb1fb2..1f7292e 100644 --- a/src/components/resources/ResourceCard.vue +++ b/src/components/resources/ResourceCard.vue @@ -12,6 +12,7 @@ import { useS3ObjectStore } from "@/stores/s3objects"; import { useResourceStore } from "@/stores/resources"; import { Tooltip } from "bootstrap"; import { useNameStore } from "@/stores/names"; +import RequestReviewButton from "@/components/resources/RequestReviewButton.vue"; const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const objectRepository = useS3ObjectStore(); @@ -98,7 +99,7 @@ onMounted(() => { ...(document .querySelector("#resource-card-" + randomIDSuffix) ?.querySelectorAll("[data-bs-toggle='tooltip']") ?? []), - ].map((el) => new Tooltip(el)); + ].forEach((el) => new Tooltip(el)); } }); </script> @@ -224,33 +225,20 @@ onMounted(() => { props.extended && resourceVersion.status == Status.RESOURCE_REQUESTED " + class="d-flex justify-content-between align-items-center" > - <div class="btn-group" role="group"> - <button - type="button" - class="btn btn-primary" - :disabled=" - !resourceVersionS3Ready[ - resourceVersion.resource_version_id - ] - " - @click="requestReview(resourceVersion)" - > - Request Review - </button> - <button - type="button" - class="btn btn-primary" - @click="clickCheckS3Resource(resourceVersion)" - > - <font-awesome-icon - icon="fa-solid fa-arrow-rotate-right" - /> - </button> - </div> + <request-review-button + :disabled=" + !resourceVersionS3Ready[ + resourceVersion.resource_version_id + ] + " + @click-refresh="clickCheckS3Resource(resourceVersion)" + @click-review="requestReview(resourceVersion)" + /> <button type="button" - class="btn btn-info btn-sm float-end" + class="btn btn-info btn-sm" data-bs-toggle="modal" data-bs-target="#uploadResourceInfoModal" @click="emit('click-info', resourceVersion)" diff --git a/src/components/resources/ResourceVersionInfoModal.vue b/src/components/resources/modals/ResourceVersionInfoModal.vue similarity index 100% rename from src/components/resources/ResourceVersionInfoModal.vue rename to src/components/resources/modals/ResourceVersionInfoModal.vue diff --git a/src/components/resources/modals/UploadResourceInfoModal.vue b/src/components/resources/modals/UploadResourceInfoModal.vue index 10282ae..3b33bd3 100644 --- a/src/components/resources/modals/UploadResourceInfoModal.vue +++ b/src/components/resources/modals/UploadResourceInfoModal.vue @@ -10,6 +10,7 @@ import { useS3ObjectStore } from "@/stores/s3objects"; import { useResourceStore } from "@/stores/resources"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { Modal } from "bootstrap"; +import RequestReviewButton from "@/components/resources/RequestReviewButton.vue"; const props = defineProps<{ modalId: string; @@ -160,6 +161,7 @@ onMounted(() => { </p> </li> <li + class="pb-2" :class="{ 'text-decoration-line-through': resourceReviewEnabled, }" @@ -298,47 +300,45 @@ s3 = boto3.resource( /> </b> </template> + <copy-to-clipboard-icon + v-if="props.resourceVersion && !resourceReviewEnabled" + button + :text="codeExample" + /> </li> <li> <h6>Request review</h6> <p> Click <b>Request Review</b> to request a review of the resource. </p> - <div class="btn-group mb-2" role="group" v-if="props.resourceVersion"> - <button - type="button" - class="btn btn-primary" - :disabled="!resourceReviewEnabled" - @click="requestReview(props.resourceVersion)" - > - Request Review - </button> - <button - v-if="props.resourceVersion.status === Status.RESOURCE_REQUESTED" - type="button" - class="btn btn-primary" - @click="clickCheckS3Resource(props.resourceVersion)" - > - <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" /> - </button> - </div> + <request-review-button + v-if="props.resourceVersion?.status === Status.RESOURCE_REQUESTED" + class="mb-2" + :disabled="!resourceReviewEnabled" + @click-review="requestReview(props.resourceVersion)" + @click-refresh="clickCheckS3Resource(props.resourceVersion)" + /> + </li> + <li> + <h6>Request resource synchronization</h6> + <p> + Once a Reviewer approves your resource, the resource will be + publicly visible in CloWM. To use it in during a workflow execution, + you can request the synchronization of the resource to the cluster. + </p> </li> <li> <h6>Resource availability</h6> <p> - Once a Reviewer approves your resource synchronization request, the - resource will be made available in CloWM and is accessible for every - workflow via its <b>Nextflow Access Path</b>. + When an administrator approves the synchronization request and the + resource is download to the cluster, the resource will now available + for a workflow execution and is accessible for every workflow via + its <b>Nextflow Access Path</b>. </p> </li> </ol> </template> <template #footer> - <copy-to-clipboard-icon - v-if="props.resourceVersion && !resourceReviewEnabled" - button - :text="codeExample" - /> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button> diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue index dc30668..1afe153 100644 --- a/src/components/workflows/WorkflowWithVersionsCard.vue +++ b/src/components/workflows/WorkflowWithVersionsCard.vue @@ -218,7 +218,7 @@ onMounted(() => { <th scope="col">Updated at</th> <th scope="col" class="text-align-center">Usage</th> <th scope="col" class="text-align-center">Icon</th> - <th scope="col">Link</th> + <th scope="col"></th> </tr> </thead> <tbody class="table-group-divider"> @@ -287,31 +287,45 @@ onMounted(() => { data-bs-toggle="modal" data-bs-target="#updateWorkflowVersionIconModal" /> + </td> + <td class="text-end"> <font-awesome-icon - v-else - icon="fa-solid fa-circle-plus" - class="add-icon-hover cursor-pointer" - @click="emit('workflow-update-icon-click', version)" - data-bs-toggle="modal" - data-bs-target="#updateWorkflowVersionIconModal" + icon="fa-solid fa-bars" + data-bs-toggle="dropdown" + aria-expanded="false" + class="cursor-pointer p-1" /> - </td> - <td> - <router-link - class="w-fit mx-0" - :to="{ - name: 'workflow-version', - params: { - workflowId: props.workflow.workflow_id, - versionId: version.workflow_version_id, - }, - query: { - workflowModeId: version.modes?.[0] ?? undefined, - developerView: 'true', - }, - }" - >View - </router-link> + <ul class="dropdown-menu dropdown-menu-end"> + <li> + <router-link + class="dropdown-item" + :to="{ + name: 'workflow-version', + params: { + workflowId: props.workflow.workflow_id, + versionId: version.workflow_version_id, + }, + query: { + workflowModeId: version.modes?.[0] ?? undefined, + developerView: 'true', + }, + }" + >View + </router-link> + </li> + <li> + <a + class="dropdown-item" + href="#" + data-bs-toggle="modal" + data-bs-target="#updateWorkflowVersionIconModal" + @click.prevent=" + emit('workflow-update-icon-click', version) + " + >Update icon</a + > + </li> + </ul> </td> </tr> <tr> diff --git a/src/views/admin/AdminResourcesView.vue b/src/views/admin/AdminResourcesView.vue index b371e7e..409ae18 100644 --- a/src/views/admin/AdminResourcesView.vue +++ b/src/views/admin/AdminResourcesView.vue @@ -11,7 +11,7 @@ import type { User } from "@/client/auth"; import { useNameStore } from "@/stores/names"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import dayjs from "dayjs"; -import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue"; +import ResourceVersionInfoModal from "@/components/resources/modals/ResourceVersionInfoModal.vue"; const resourceRepository = useResourceStore(); const nameRepository = useNameStore(); diff --git a/src/views/admin/AdminSyncRequestsView.vue b/src/views/admin/AdminSyncRequestsView.vue index 5774737..bb7c0f6 100644 --- a/src/views/admin/AdminSyncRequestsView.vue +++ b/src/views/admin/AdminSyncRequestsView.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import BootstrapToast from "@/components/BootstrapToast.vue"; import ReasonModal from "@/components/modals/ReasonModal.vue"; -import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue"; +import ResourceVersionInfoModal from "@/components/resources/modals/ResourceVersionInfoModal.vue"; import { onMounted, reactive } from "vue"; import { type ResourceOut, diff --git a/src/views/resources/ListResourcesView.vue b/src/views/resources/ListResourcesView.vue index c764868..7cea20a 100644 --- a/src/views/resources/ListResourcesView.vue +++ b/src/views/resources/ListResourcesView.vue @@ -118,15 +118,15 @@ onMounted(() => { /> </div> </div> - <div class="form-check fs-5 ms-auto"> + <div class="form-check form-check-reverse form-check-inline fs-6 ms-auto"> + <input + class="form-check-input" + type="checkbox" + v-model="resourceState.showPrivate" + id="public-resources-checkbox" + /> <label class="form-check-label" for="public-resources-checkbox"> Show only public resources - <input - class="form-check-input" - type="checkbox" - v-model="resourceState.showPrivate" - id="public-resources-checkbox" - /> </label> </div> <font-awesome-icon diff --git a/src/views/resources/ReviewResourceView.vue b/src/views/resources/ReviewResourceView.vue index 5c863c3..5b7c226 100644 --- a/src/views/resources/ReviewResourceView.vue +++ b/src/views/resources/ReviewResourceView.vue @@ -8,7 +8,7 @@ import { } from "@/client/resource"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { Modal, Toast, Tooltip } from "bootstrap"; -import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue"; +import ResourceVersionInfoModal from "@/components/resources/modals/ResourceVersionInfoModal.vue"; import { useNameStore } from "@/stores/names"; import BootstrapToast from "@/components/BootstrapToast.vue"; import ReasonModal from "@/components/modals/ReasonModal.vue"; diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue index 89632a1..34fb0c0 100644 --- a/src/views/workflows/MyWorkflowsView.vue +++ b/src/views/workflows/MyWorkflowsView.vue @@ -114,7 +114,7 @@ onMounted(() => { modal-id="updateWorkflowCredentialsModal" /> <div - class="row border-bottom mb-4 justify-content-between align-items-center pb-2" + class="row border-bottom mb-4 justify-content-between align-items-center pb-2 pe-2" > <h2 class="w-fit">My Workflows</h2> <button diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue index ad15851..e8420b3 100644 --- a/src/views/workflows/ReviewWorkflowsView.vue +++ b/src/views/workflows/ReviewWorkflowsView.vue @@ -35,10 +35,6 @@ function updateWorkflowVersionStatus( }); } -function isDefined<T>(argument: T | undefined | null): argument is T { - return argument != undefined; -} - onMounted(() => { workflowRepository .fetchReviewableWorkflows(() => { @@ -55,9 +51,7 @@ onMounted(() => { }, 1000); return workflows; }) - .then((workflows) => - workflows.map((workflow) => workflow.developer_id).filter(isDefined), - ) + .then((workflows) => workflows.map((workflow) => workflow.developer_id)) .then(userRepository.fetchUsernames); }); </script> diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue index 28d53d4..32dfd64 100644 --- a/src/views/workflows/WorkflowView.vue +++ b/src/views/workflows/WorkflowView.vue @@ -16,9 +16,11 @@ import { import { determineGitIcon } from "@/utils/GitRepository"; import { useAuthStore } from "@/stores/users"; import { useWorkflowStore } from "@/stores/workflows"; +import { useNameStore } from "@/stores/names"; const workflowRepository = useWorkflowStore(); const userRepository = useAuthStore(); +const nameRepository = useNameStore(); // Props // ============================================================================= @@ -132,6 +134,10 @@ function updateWorkflow(workflowId: string) { workflowState.loading = false; workflowState.initialOpen = false; }) + .then((workflow) => { + userRepository.fetchUsernames([workflow.developer_id]); + return workflow; + }) .then((workflow) => { document.title = workflow.name + " - CloWM"; if (props.versionId == undefined) { @@ -230,6 +236,9 @@ onMounted(() => { <h3 class="w-fit"> {{ workflow.name }} <span v-if="activeVersionString">@{{ activeVersionString }}</span> + <span v-if="nameRepository.getName(workflow.developer_id)" class="fs-4"> + by {{ nameRepository.getName(workflow.developer_id) }}</span + > </h3> <img v-if="activeVersionIcon != null" -- GitLab