Skip to content
Snippets Groups Projects
Verified Commit e28a3a9b authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Update resource info modal to new flow

parent 81aa8cd6
No related branches found
No related tags found
1 merge request!97Resolve "Adapt UI to new Resoruce creation flow"
This commit is part of merge request !97. Comments created here will be created in the context of that merge request.
Showing
with 148 additions and 95 deletions
...@@ -27,7 +27,7 @@ export type WorkflowOut = { ...@@ -27,7 +27,7 @@ export type WorkflowOut = {
/** /**
* ID of developer of the workflow * ID of developer of the workflow
*/ */
developer_id?: (string | null); developer_id: string;
/** /**
* Flag if the workflow is hosted in a private git repository * Flag if the workflow is hosted in a private git repository
*/ */
......
...@@ -45,7 +45,11 @@ onMounted(() => { ...@@ -45,7 +45,11 @@ onMounted(() => {
color-class="danger" color-class="danger"
>Can't copy to clipboard >Can't copy to clipboard
</bootstrap-toast> </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 Copy to Clipboard
<font-awesome-icon icon="fa-solid fa-clipboard" class="ms-1" /> <font-awesome-icon icon="fa-solid fa-clipboard" class="ms-1" />
</button> </button>
......
<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>
...@@ -12,6 +12,7 @@ import { useS3ObjectStore } from "@/stores/s3objects"; ...@@ -12,6 +12,7 @@ import { useS3ObjectStore } from "@/stores/s3objects";
import { useResourceStore } from "@/stores/resources"; import { useResourceStore } from "@/stores/resources";
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import { useNameStore } from "@/stores/names"; import { useNameStore } from "@/stores/names";
import RequestReviewButton from "@/components/resources/RequestReviewButton.vue";
const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const randomIDSuffix: string = Math.random().toString(16).substring(2, 8);
const objectRepository = useS3ObjectStore(); const objectRepository = useS3ObjectStore();
...@@ -98,7 +99,7 @@ onMounted(() => { ...@@ -98,7 +99,7 @@ onMounted(() => {
...(document ...(document
.querySelector("#resource-card-" + randomIDSuffix) .querySelector("#resource-card-" + randomIDSuffix)
?.querySelectorAll("[data-bs-toggle='tooltip']") ?? []), ?.querySelectorAll("[data-bs-toggle='tooltip']") ?? []),
].map((el) => new Tooltip(el)); ].forEach((el) => new Tooltip(el));
} }
}); });
</script> </script>
...@@ -224,33 +225,20 @@ onMounted(() => { ...@@ -224,33 +225,20 @@ onMounted(() => {
props.extended && props.extended &&
resourceVersion.status == Status.RESOURCE_REQUESTED resourceVersion.status == Status.RESOURCE_REQUESTED
" "
class="d-flex justify-content-between align-items-center"
> >
<div class="btn-group" role="group"> <request-review-button
<button :disabled="
type="button" !resourceVersionS3Ready[
class="btn btn-primary" resourceVersion.resource_version_id
:disabled=" ]
!resourceVersionS3Ready[ "
resourceVersion.resource_version_id @click-refresh="clickCheckS3Resource(resourceVersion)"
] @click-review="requestReview(resourceVersion)"
" />
@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>
<button <button
type="button" type="button"
class="btn btn-info btn-sm float-end" class="btn btn-info btn-sm"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#uploadResourceInfoModal" data-bs-target="#uploadResourceInfoModal"
@click="emit('click-info', resourceVersion)" @click="emit('click-info', resourceVersion)"
......
...@@ -10,6 +10,7 @@ import { useS3ObjectStore } from "@/stores/s3objects"; ...@@ -10,6 +10,7 @@ import { useS3ObjectStore } from "@/stores/s3objects";
import { useResourceStore } from "@/stores/resources"; import { useResourceStore } from "@/stores/resources";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { Modal } from "bootstrap"; import { Modal } from "bootstrap";
import RequestReviewButton from "@/components/resources/RequestReviewButton.vue";
const props = defineProps<{ const props = defineProps<{
modalId: string; modalId: string;
...@@ -160,6 +161,7 @@ onMounted(() => { ...@@ -160,6 +161,7 @@ onMounted(() => {
</p> </p>
</li> </li>
<li <li
class="pb-2"
:class="{ :class="{
'text-decoration-line-through': resourceReviewEnabled, 'text-decoration-line-through': resourceReviewEnabled,
}" }"
...@@ -298,47 +300,45 @@ s3 = boto3.resource( ...@@ -298,47 +300,45 @@ s3 = boto3.resource(
/> />
</b> </b>
</template> </template>
<copy-to-clipboard-icon
v-if="props.resourceVersion && !resourceReviewEnabled"
button
:text="codeExample"
/>
</li> </li>
<li> <li>
<h6>Request review</h6> <h6>Request review</h6>
<p> <p>
Click <b>Request Review</b> to request a review of the resource. Click <b>Request Review</b> to request a review of the resource.
</p> </p>
<div class="btn-group mb-2" role="group" v-if="props.resourceVersion"> <request-review-button
<button v-if="props.resourceVersion?.status === Status.RESOURCE_REQUESTED"
type="button" class="mb-2"
class="btn btn-primary" :disabled="!resourceReviewEnabled"
:disabled="!resourceReviewEnabled" @click-review="requestReview(props.resourceVersion)"
@click="requestReview(props.resourceVersion)" @click-refresh="clickCheckS3Resource(props.resourceVersion)"
> />
Request Review </li>
</button> <li>
<button <h6>Request resource synchronization</h6>
v-if="props.resourceVersion.status === Status.RESOURCE_REQUESTED" <p>
type="button" Once a Reviewer approves your resource, the resource will be
class="btn btn-primary" publicly visible in CloWM. To use it in during a workflow execution,
@click="clickCheckS3Resource(props.resourceVersion)" you can request the synchronization of the resource to the cluster.
> </p>
<font-awesome-icon icon="fa-solid fa-arrow-rotate-right" />
</button>
</div>
</li> </li>
<li> <li>
<h6>Resource availability</h6> <h6>Resource availability</h6>
<p> <p>
Once a Reviewer approves your resource synchronization request, the When an administrator approves the synchronization request and the
resource will be made available in CloWM and is accessible for every resource is download to the cluster, the resource will now available
workflow via its <b>Nextflow Access Path</b>. for a workflow execution and is accessible for every workflow via
its <b>Nextflow Access Path</b>.
</p> </p>
</li> </li>
</ol> </ol>
</template> </template>
<template #footer> <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"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close Close
</button> </button>
......
...@@ -218,7 +218,7 @@ onMounted(() => { ...@@ -218,7 +218,7 @@ onMounted(() => {
<th scope="col">Updated at</th> <th scope="col">Updated at</th>
<th scope="col" class="text-align-center">Usage</th> <th scope="col" class="text-align-center">Usage</th>
<th scope="col" class="text-align-center">Icon</th> <th scope="col" class="text-align-center">Icon</th>
<th scope="col">Link</th> <th scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody class="table-group-divider"> <tbody class="table-group-divider">
...@@ -287,31 +287,45 @@ onMounted(() => { ...@@ -287,31 +287,45 @@ onMounted(() => {
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#updateWorkflowVersionIconModal" data-bs-target="#updateWorkflowVersionIconModal"
/> />
</td>
<td class="text-end">
<font-awesome-icon <font-awesome-icon
v-else icon="fa-solid fa-bars"
icon="fa-solid fa-circle-plus" data-bs-toggle="dropdown"
class="add-icon-hover cursor-pointer" aria-expanded="false"
@click="emit('workflow-update-icon-click', version)" class="cursor-pointer p-1"
data-bs-toggle="modal"
data-bs-target="#updateWorkflowVersionIconModal"
/> />
</td> <ul class="dropdown-menu dropdown-menu-end">
<td> <li>
<router-link <router-link
class="w-fit mx-0" class="dropdown-item"
:to="{ :to="{
name: 'workflow-version', name: 'workflow-version',
params: { params: {
workflowId: props.workflow.workflow_id, workflowId: props.workflow.workflow_id,
versionId: version.workflow_version_id, versionId: version.workflow_version_id,
}, },
query: { query: {
workflowModeId: version.modes?.[0] ?? undefined, workflowModeId: version.modes?.[0] ?? undefined,
developerView: 'true', developerView: 'true',
}, },
}" }"
>View >View
</router-link> </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> </td>
</tr> </tr>
<tr> <tr>
......
...@@ -11,7 +11,7 @@ import type { User } from "@/client/auth"; ...@@ -11,7 +11,7 @@ import type { User } from "@/client/auth";
import { useNameStore } from "@/stores/names"; import { useNameStore } from "@/stores/names";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue"; import ResourceVersionInfoModal from "@/components/resources/modals/ResourceVersionInfoModal.vue";
const resourceRepository = useResourceStore(); const resourceRepository = useResourceStore();
const nameRepository = useNameStore(); const nameRepository = useNameStore();
......
<script setup lang="ts"> <script setup lang="ts">
import BootstrapToast from "@/components/BootstrapToast.vue"; import BootstrapToast from "@/components/BootstrapToast.vue";
import ReasonModal from "@/components/modals/ReasonModal.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 { onMounted, reactive } from "vue";
import { import {
type ResourceOut, type ResourceOut,
......
...@@ -118,15 +118,15 @@ onMounted(() => { ...@@ -118,15 +118,15 @@ onMounted(() => {
/> />
</div> </div>
</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"> <label class="form-check-label" for="public-resources-checkbox">
Show only public resources Show only public resources
<input
class="form-check-input"
type="checkbox"
v-model="resourceState.showPrivate"
id="public-resources-checkbox"
/>
</label> </label>
</div> </div>
<font-awesome-icon <font-awesome-icon
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
} from "@/client/resource"; } from "@/client/resource";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { Modal, Toast, Tooltip } from "bootstrap"; 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 { useNameStore } from "@/stores/names";
import BootstrapToast from "@/components/BootstrapToast.vue"; import BootstrapToast from "@/components/BootstrapToast.vue";
import ReasonModal from "@/components/modals/ReasonModal.vue"; import ReasonModal from "@/components/modals/ReasonModal.vue";
......
...@@ -114,7 +114,7 @@ onMounted(() => { ...@@ -114,7 +114,7 @@ onMounted(() => {
modal-id="updateWorkflowCredentialsModal" modal-id="updateWorkflowCredentialsModal"
/> />
<div <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> <h2 class="w-fit">My Workflows</h2>
<button <button
......
...@@ -35,10 +35,6 @@ function updateWorkflowVersionStatus( ...@@ -35,10 +35,6 @@ function updateWorkflowVersionStatus(
}); });
} }
function isDefined<T>(argument: T | undefined | null): argument is T {
return argument != undefined;
}
onMounted(() => { onMounted(() => {
workflowRepository workflowRepository
.fetchReviewableWorkflows(() => { .fetchReviewableWorkflows(() => {
...@@ -55,9 +51,7 @@ onMounted(() => { ...@@ -55,9 +51,7 @@ onMounted(() => {
}, 1000); }, 1000);
return workflows; return workflows;
}) })
.then((workflows) => .then((workflows) => workflows.map((workflow) => workflow.developer_id))
workflows.map((workflow) => workflow.developer_id).filter(isDefined),
)
.then(userRepository.fetchUsernames); .then(userRepository.fetchUsernames);
}); });
</script> </script>
......
...@@ -16,9 +16,11 @@ import { ...@@ -16,9 +16,11 @@ import {
import { determineGitIcon } from "@/utils/GitRepository"; import { determineGitIcon } from "@/utils/GitRepository";
import { useAuthStore } from "@/stores/users"; import { useAuthStore } from "@/stores/users";
import { useWorkflowStore } from "@/stores/workflows"; import { useWorkflowStore } from "@/stores/workflows";
import { useNameStore } from "@/stores/names";
const workflowRepository = useWorkflowStore(); const workflowRepository = useWorkflowStore();
const userRepository = useAuthStore(); const userRepository = useAuthStore();
const nameRepository = useNameStore();
// Props // Props
// ============================================================================= // =============================================================================
...@@ -132,6 +134,10 @@ function updateWorkflow(workflowId: string) { ...@@ -132,6 +134,10 @@ function updateWorkflow(workflowId: string) {
workflowState.loading = false; workflowState.loading = false;
workflowState.initialOpen = false; workflowState.initialOpen = false;
}) })
.then((workflow) => {
userRepository.fetchUsernames([workflow.developer_id]);
return workflow;
})
.then((workflow) => { .then((workflow) => {
document.title = workflow.name + " - CloWM"; document.title = workflow.name + " - CloWM";
if (props.versionId == undefined) { if (props.versionId == undefined) {
...@@ -230,6 +236,9 @@ onMounted(() => { ...@@ -230,6 +236,9 @@ onMounted(() => {
<h3 class="w-fit"> <h3 class="w-fit">
{{ workflow.name }} {{ workflow.name }}
<span v-if="activeVersionString">@{{ activeVersionString }}</span> <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> </h3>
<img <img
v-if="activeVersionIcon != null" v-if="activeVersionIcon != null"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment