diff --git a/src/App.vue b/src/App.vue index 3fa22bb360193ea55cc864b9533df708087c4e85..306e0d67f5bb8368dfa5251bf8a3638cfaa6da88 100644 --- a/src/App.vue +++ b/src/App.vue @@ -69,9 +69,17 @@ onBeforeMount(() => { <template> <NavbarTop /> <div class="container-xxl mt-4 flex-grow-1"> + <div + id="global-toast-container" + class="toast-container position-fixed top-toast end-0 p-3" + ></div> <router-view></router-view> </div> <FooterBottom /> </template> -<style scoped></style> +<style scoped> +.top-toast { + top: 4rem; +} +</style> diff --git a/src/assets/main.css b/src/assets/main.css index 24f661e1ba5b45e145150f636947b284828c9656..5197334f91704d2434152d9854af2c6a8ad5c74f 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -4,10 +4,6 @@ font-size: 3.5rem; } -.top-toast { - top: 4rem; -} - .w-fit { width: fit-content; } diff --git a/src/components/BootstrapToast.vue b/src/components/BootstrapToast.vue new file mode 100644 index 0000000000000000000000000000000000000000..cc2f27fa4ccb1d85280c05efe080bd4ea0e240c7 --- /dev/null +++ b/src/components/BootstrapToast.vue @@ -0,0 +1,60 @@ +<script setup lang="ts"> +import { useSlots, computed } from "vue"; + +const slots = useSlots(); +const props = defineProps({ + colorClass: { type: String, required: false, default: "success" }, + toastId: { type: String, required: true }, +}); + +const colorClassName = computed<string>(() => { + return "text-bg-" + props.colorClass; +}); +const emit = defineEmits<{ + (e: "hidden.bs.toast"): void; +}>(); +</script> + +<template> + <Teleport to="#global-toast-container"> + <div + role="alert" + aria-live="assertive" + aria-atomic="true" + class="toast align-items-center border-0" + :class="colorClassName" + data-bs-autohide="true" + :id="props.toastId" + v-on="{ 'hidden.bs.toast': () => emit('hidden.bs.toast') }" + > + <div v-if="slots.body" class="toast-header" :class="colorClassName"> + <div class="me-auto"> + <slot></slot> + </div> + <button + type="button" + class="btn-close btn-close-white me-2 m-auto" + data-bs-dismiss="toast" + aria-label="Close" + ></button> + </div> + <div v-else class="d-flex"> + <div class="toast-body"> + <slot></slot> + </div> + <button + type="button" + class="btn-close btn-close-white me-2 m-auto" + data-bs-dismiss="toast" + aria-label="Close" + ></button> + </div> + + <div class="toast-body" v-if="slots.body"> + <slot name="body"></slot> + </div> + </div> + </Teleport> +</template> + +<style scoped></style> diff --git a/src/components/CopyToClipboardIcon.vue b/src/components/CopyToClipboardIcon.vue index 9d28cbaac691f1c1a6f8098a4e48396144561203..ca9627edae2f091439e74c99253b5baf85519d72 100644 --- a/src/components/CopyToClipboardIcon.vue +++ b/src/components/CopyToClipboardIcon.vue @@ -2,6 +2,7 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { onMounted } from "vue"; import { Toast, Tooltip } from "bootstrap"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const props = defineProps<{ text: string; @@ -33,44 +34,14 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully copied to clipboard</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - :id="'failToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Can't copy to clipboard</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix" + >Successfully copied to clipboard</bootstrap-toast + > + <bootstrap-toast + :toast-id="'failToast-' + randomIDSuffix" + color-class="danger" + >Can't copy to clipboard + </bootstrap-toast> <span class="hover-info cursor-pointer" data-bs-toggle="tooltip" diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue index 0687aaa6a9ceee3c848e94eac1a588108848a6fb..b51e17dad74e5b6eeb21f8094126dc7f33cec902 100644 --- a/src/components/object-storage/modals/CopyObjectModal.vue +++ b/src/components/object-storage/modals/CopyObjectModal.vue @@ -5,6 +5,7 @@ import { onMounted, reactive, watch } from "vue"; import type { _Object as S3Object } from "@aws-sdk/client-s3"; import { useBucketStore } from "@/stores/buckets"; import { useS3ObjectStore } from "@/stores/s3objects"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const objectRepository = useS3ObjectStore(); @@ -83,49 +84,16 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully copied file</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - :id="'errorToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body"> - There has been some Error.<br /> - Try again later - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully copied file + </bootstrap-toast> + <bootstrap-toast + :toast-id="'errorToast-' + randomIDSuffix" + color-class="danger" + > + There has been some Error.<br /> + Try again later + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue index cd26d2c2a94ff03d6abe9a68e0f2fb5e623a081a..12400a92ddfd07598ff23c6f69779616f207fc1a 100644 --- a/src/components/object-storage/modals/CreateFolderModal.vue +++ b/src/components/object-storage/modals/CreateFolderModal.vue @@ -4,6 +4,7 @@ import { computed, onMounted, reactive } from "vue"; import { Modal, Toast } from "bootstrap"; import { useS3ObjectStore } from "@/stores/s3objects"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const objectRepository = useS3ObjectStore(); @@ -67,49 +68,16 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully created Folder</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - :id="'errorToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body"> - There has been some Error.<br /> - Try again later - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully created Folder + </bootstrap-toast> + <bootstrap-toast + :toast-id="'errorToast-' + randomIDSuffix" + color-class="danger" + > + There has been some Error.<br /> + Try again later + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue index 60ad031740571fd138c8cb918a68137d4bf94259..4ce35b44058b6658ec624818b2f30e85736fdd0a 100644 --- a/src/components/object-storage/modals/PermissionModal.vue +++ b/src/components/object-storage/modals/PermissionModal.vue @@ -16,6 +16,7 @@ import { Permission } from "@/client/s3proxy"; import { Toast } from "bootstrap"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { useBucketStore } from "@/stores/buckets"; +import BootstrapToast from "@/components/BootstrapToast.vue"; // Props // ----------------------------------------------------------------------------- @@ -271,33 +272,16 @@ function toTimestampChanged(target?: HTMLInputElement | null) { :back-modal-id="modalID" @user-found="updateUser" /> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'toast-' + randomIDSuffix" - v-on="{ 'hidden.bs.toast': toastHidden }" - > - <div class="d-flex"> - <div class="toast-body"> - Successfully - <span v-if="permissionDeleted">deleted</span> - <span v-else-if="editPermission">edited</span> - <span v-else>created</span> - Permission - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast + :toast-id="'toast-' + randomIDSuffix" + v-on="{ 'hidden.bs.toast': toastHidden }" + > + Successfully + <template v-if="permissionDeleted">deleted</template> + <template v-else-if="editPermission">edited</template> + <template v-else>created</template> + Permission + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue index adafe9a20af1735e997005d1737f9b3dc2edaa1b..dfa6f4d75571593127f7b874031ef4fc87a9988e 100644 --- a/src/components/object-storage/modals/UploadObjectModal.vue +++ b/src/components/object-storage/modals/UploadObjectModal.vue @@ -4,6 +4,7 @@ import { computed, onMounted, reactive, ref, watch } from "vue"; import { Modal, Toast } from "bootstrap"; import { partial } from "filesize"; import { useS3ObjectStore } from "@/stores/s3objects"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const fsize = partial({ base: 2, standard: "jedec" }); const objectRepository = useS3ObjectStore(); @@ -101,49 +102,16 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully uploaded file</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - :id="'errorToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body"> - There has been some Error.<br /> - Try again later - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully uploaded file + </bootstrap-toast> + <bootstrap-toast + :toast-id="'errorToast-' + randomIDSuffix" + color-class="danger" + > + There has been some Error.<br /> + Try again later + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue index 7546e8bbb109e223955dd0c548f8ca097799c33d..69dd3cd636bd7fc91331f36de104d7f2ab203b65 100644 --- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue +++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue @@ -8,6 +8,7 @@ import ParameterStringInput from "@/components/parameter-schema/form-mode/Parame import { Toast } from "bootstrap"; import { useBucketStore } from "@/stores/buckets"; import { useS3KeyStore } from "@/stores/s3keys"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const bucketRepository = useBucketStore(); const s3KeyRepository = useS3KeyStore(); @@ -186,34 +187,18 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - id="workflowExecutionErrorToast" - > - <div class="d-flex p-2 justify-content-between align-items-center"> - <div class="toast-body"> - <template v-if="formState.errorType === 'form'"> - Some inputs are not valid. - </template> - <template v-else> - There was an error with starting the workflow execution. Look in the - console for more information. - </template> - </div> - <button - type="button" - class="btn-close btn-close-white" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast toast-id="workflowExecutionErrorToast" color-class="danger"> + <template>Error starting workflow</template> + <template #body> + <template v-if="formState.errorType === 'form'"> + Some inputs are not valid. + </template> + <template v-else> + There was an error with starting the workflow execution. Look in the + console for more information. + </template> + </template> + </bootstrap-toast> <div class="row mb-5 align-items-start"> <form v-if="props.schema" diff --git a/src/components/resources/createResourceModal.vue b/src/components/resources/CreateResourceModal.vue similarity index 98% rename from src/components/resources/createResourceModal.vue rename to src/components/resources/CreateResourceModal.vue index 27dc54ce04625c82776536b59505eb775c45618a..0f4e66decbabc00cf016b9b6bc77de7c069adc01 100644 --- a/src/components/resources/createResourceModal.vue +++ b/src/components/resources/CreateResourceModal.vue @@ -34,7 +34,7 @@ const props = defineProps<{ let createResourceModal: Modal | null = null; onMounted(() => { - createResourceModal = new Modal("#" + props.modalID); + CreateResourceModal = new Modal("#" + props.modalID); }); function createResource() { @@ -49,7 +49,7 @@ function createResource() { resourceRepository .createResource(resource) .then(() => { - createResourceModal?.hide(); + CreateResourceModal?.hide(); resource.name = ""; resource.description = ""; resource.source = ""; diff --git a/src/components/resources/ResourceCard.vue b/src/components/resources/ResourceCard.vue index f3c9140924c3c5f9fb01e34e0649d8440c434d79..3aa21f21d83f3d2532a08f180d9d6d3ba322291f 100644 --- a/src/components/resources/ResourceCard.vue +++ b/src/components/resources/ResourceCard.vue @@ -7,6 +7,8 @@ import { import { computed } from "vue"; import dayjs from "dayjs"; import { useAuthStore } from "@/stores/users"; +import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const userRepository = useAuthStore(); @@ -14,6 +16,7 @@ const userRepository = useAuthStore(); const props = defineProps<{ resource: ResourceOut; loading: boolean; + extended?: boolean; }>(); const resourceVersions = computed<ResourceVersionOut[]>( @@ -86,14 +89,74 @@ const resourceVersions = computed<ResourceVersionOut[]>( :data-bs-parent="'#accordion-' + props.resource.resource_id" > <div class="accordion-body"> - <p> + <div> Last Updated: {{ dayjs.unix(resourceVersion.created_at).format("DD MMM YYYY") }} - </p> - <div> - Nextflow Access Path: <br />{{ resourceVersion.cluster_path }} + </div> + <div + v-if=" + resourceVersion.status === Status.SYNCHRONIZED || + resourceVersion.status === Status.LATEST + " + class="my-1" + > + <label + :for=" + 'nextflow-access-path-' + + resourceVersion.resource_version_id + " + class="form-label" + >Nextflow Access Path:</label + > + <div class="input-group fs-4 mb-3"> + <input + :id=" + 'nextflow-access-path-' + + resourceVersion.resource_version_id + " + class="form-control" + type="text" + :value="resourceVersion.cluster_path" + aria-label="Nextflow Access Path" + readonly + /> + <span class="input-group-text" + ><copy-to-clipboard-icon + :text="resourceVersion.cluster_path" + /></span> + </div> + </div> + <div + v-if=" + props.extended && + resourceVersion.status !== Status.S3_DELETED + " + class="my-1" + > + <label + :for=" + 's3-access-path-' + resourceVersion.resource_version_id + " + class="form-label" + >S3 Path:</label + > + <div class="input-group fs-4 mb-3"> + <input + :id=" + 's3-access-path-' + resourceVersion.resource_version_id + " + class="form-control" + type="text" + :value="resourceVersion.s3_path" + aria-label="S3 Access Path" + readonly + /> + <span class="input-group-text" + ><copy-to-clipboard-icon :text="resourceVersion.s3_path" + /></span> + </div> </div> </div> </div> diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue index 86bd5862fcfe811428259c97758a9fb9ca299ee7..7c24bca94610127acde85bb4d3bcaa635158ca5f 100644 --- a/src/components/workflows/modals/CreateWorkflowModal.vue +++ b/src/components/workflows/modals/CreateWorkflowModal.vue @@ -17,6 +17,7 @@ import { import { valid } from "semver"; import WorkflowModeTransitionGroup from "@/components/transitions/WorkflowModeTransitionGroup.vue"; import { useWorkflowStore } from "@/stores/workflows"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const workflowRepository = useWorkflowStore(); // Emitted Events @@ -319,26 +320,9 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully created Workflow</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully created Workflow + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue index 43f057f9103cf72bdc387ce8b0896f64542786c9..130f275792f865f75104b080ecb3b2a37bc6b9b9 100644 --- a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue @@ -7,6 +7,7 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import DeleteModal from "@/components/modals/DeleteModal.vue"; import { GitRepository } from "@/utils/GitRepository"; import { useWorkflowStore } from "@/stores/workflows"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const workflowRepository = useWorkflowStore(); // Constants @@ -131,29 +132,9 @@ onMounted(() => { :back-modal-id="modalID" @confirm-delete="deleteCredentials()" /> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div v-if="formState.updateCredentials" class="toast-body"> - Successfully updated credentials - </div> - <div v-else class="toast-body">Successfully deleted credentials</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully updated credentials + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue index 1b393686f6bdd81ebcdf4ce82d6e975fcac7eaa4..78ca17fc52d89f9421472de599a8b836615ead4c 100644 --- a/src/components/workflows/modals/UpdateWorkflowModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowModal.vue @@ -21,6 +21,7 @@ import { valid, lte, inc } from "semver"; import { latestVersion as calculateLatestVersion } from "@/utils/Workflow"; import WorkflowModeTransitionGroup from "@/components/transitions/WorkflowModeTransitionGroup.vue"; import { useWorkflowStore } from "@/stores/workflows"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const workflowRepository = useWorkflowStore(); // Bootstrap Elements @@ -307,26 +308,9 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body">Successfully updated Workflow</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully updated Workflow + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue index ef529298b5b71f8f3fdb059a7d05801201b1c7a1..420dfc32dab305f901663891a52bf6f6e6a77864 100644 --- a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue @@ -6,6 +6,7 @@ import { Modal, Toast } from "bootstrap"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import DeleteModal from "@/components/modals/DeleteModal.vue"; import { useWorkflowStore } from "@/stores/workflows"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const workflowRepository = useWorkflowStore(); // Constants @@ -140,29 +141,10 @@ onMounted(() => { :back-modal-id="modalID" @confirm-delete="deleteIcon()" /> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div v-if="formState.uploadIcon" class="toast-body"> - Successfully uploaded icon - </div> - <div v-else class="toast-body">Successfully deleted icon</div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + <div v-if="formState.uploadIcon">Successfully uploaded icon</div> + <div v-else>Successfully deleted icon</div> + </bootstrap-toast> <bootstrap-modal :modalID="modalID" :static-backdrop="true" diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue index d9e1987acd5cf60c7093f05aee006c90f5fc6ffd..ef5a66c6935b0ac6055d59a3afe0c35d7d42b194 100644 --- a/src/views/LoginView.vue +++ b/src/views/LoginView.vue @@ -4,6 +4,7 @@ import { useAuthStore } from "@/stores/users"; import { useRouter, useRoute } from "vue-router"; import { OpenAPI as AuthOpenAPI } from "@/client/auth"; import { Toast } from "bootstrap"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const router = useRouter(); const route = useRoute(); @@ -29,35 +30,16 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - style="--bs-bg-opacity: 0.7" - data-bs-config="{'delay': 8000}" - data-bs-autohide="true" - id="loginErrorToast" - > - <div class="toast-header text-bg-danger"> - <strong class="me-auto">Login Error</strong> - <button - type="button" - class="btn-close btn-close-white" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - <div class="toast-body"> - <p> - There has been some kind of error during the login.<br /> - Please try again later. - </p> - <p>Error Code: {{ route.query.login_error }}</p> - </div> - </div> - </div> + <bootstrap-toast toast-id="loginErrorToast" color-class="danger"> + <template> Login Error </template> + <template #body> + <p> + There has been some kind of error during the login.<br /> + Please try again later. + </p> + <p>Error Code: {{ route.query.login_error }}</p> + </template> + </bootstrap-toast> <div class="position-fixed start-50 translate-middle-x text-center"> <img src="/src/assets/images/clowm.svg" diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue index b3bd3768dc2e75d1d7607400d5a777f9487cc8eb..9f43cbc44fa1de1ed93971a2d578161526b85e18 100644 --- a/src/views/object-storage/BucketView.vue +++ b/src/views/object-storage/BucketView.vue @@ -21,6 +21,7 @@ import { useAuthStore } from "@/stores/users"; import { useBucketStore } from "@/stores/buckets"; import { useS3ObjectStore } from "@/stores/s3objects"; import { useS3KeyStore } from "@/stores/s3keys"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const authStore = useAuthStore(); const bucketRepository = useBucketStore(); @@ -420,28 +421,9 @@ function getObjectFileName(key: string): string { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successToast-' + randomIDSuffix" - > - <div class="d-flex"> - <div class="toast-body"> - Successfully deleted {{ deleteObjectsState.deletedItem }} - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"> + Successfully deleted {{ deleteObjectsState.deletedItem }} + </bootstrap-toast> <DeleteModal modalID="delete-object-modal" :object-name-delete="deleteObjectsState.potentialObjectToDelete" diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue index 12218064e3e4ca66cc8c1d832b4fcf47033cf9cd..1735cdb1c8d1a67448ea932770c6c5eac42fa6d2 100644 --- a/src/views/object-storage/S3KeysView.vue +++ b/src/views/object-storage/S3KeysView.vue @@ -5,6 +5,7 @@ import { reactive, onMounted, computed } from "vue"; import { useAuthStore } from "@/stores/users"; import { Toast, Tooltip } from "bootstrap"; import { useS3KeyStore } from "@/stores/s3keys"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const authStore = useAuthStore(); const keyRepository = useS3KeyStore(); @@ -76,28 +77,9 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-success align-items-center border-0" - data-bs-autohide="true" - :id="'successKeyToast'" - > - <div class="d-flex"> - <div class="toast-body"> - Successfully deleted S3 Key {{ keyState.deletedKey.slice(0, 5) }}... - </div> - <button - type="button" - class="btn-close btn-close-white me-2 m-auto" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast toast-id="successKeyToast"> + Successfully deleted S3 Key {{ keyState.deletedKey.slice(0, 5) }}... + </bootstrap-toast> <div class="row m-2 border-bottom mt-4"> <div class="col-12"></div> <h2 class="mb-2">S3 Keys</h2> diff --git a/src/views/resources/MyResourcesView.vue b/src/views/resources/MyResourcesView.vue index 3b100fc4e7e7644881e96ba12f4119458e9e6ec6..bb6bcccdef8ec7d35e0018924ae687b84472f20c 100644 --- a/src/views/resources/MyResourcesView.vue +++ b/src/views/resources/MyResourcesView.vue @@ -3,7 +3,7 @@ import { onMounted, reactive } from "vue"; import { useResourceStore } from "@/stores/resources"; import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue"; import ResourceCard from "@/components/resources/ResourceCard.vue"; -import CreateResourceModal from "@/components/resources/createResourceModal.vue"; +import CreateResourceModal from "@/components/resources/CreateResourceModal.vue"; const resourceRepository = useResourceStore(); @@ -42,7 +42,8 @@ onMounted(() => { :key="resource.resource_id" :resource="resource" :loading="false" - style="min-width: 48%" + style="width: 48%" + extended /> </CardTransitionGroup> </template> diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue index faedb73a65de83a07412979247856226b7bfd29b..e31a3389eae4d7f2eeb571fb7202f47dc1d59e3b 100644 --- a/src/views/workflows/ArbitraryWorkflowView.vue +++ b/src/views/workflows/ArbitraryWorkflowView.vue @@ -10,6 +10,7 @@ import { useWorkflowStore } from "@/stores/workflows"; import type { WorkflowIn } from "@/client/workflow"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const props = defineProps<{ wid: string; @@ -148,34 +149,21 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - id="arbitraryWorkflowExecutionViewErrorToast" - > - <div class="d-flex p-2 justify-content-between align-items-center"> - <div class="toast-body"> - <template v-if="workflowExecutionState.errorType === 'limit'"> - You have too many active workflow executions to start a new one. - </template> - <template v-else> - There was an error with starting the workflow execution. Look in the - console for more information. - </template> - </div> - <button - type="button" - class="btn-close btn-close-white" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast + toast-id="arbitraryWorkflowExecutionViewErrorToast" + color-class="danger" + > + <template> Error starting workflow </template> + <template #body> + <template v-if="workflowExecutionState.errorType === 'limit'"> + You have too many active workflow executions to start a new one. + </template> + <template v-else> + There was an error with starting the workflow execution. Look in the + console for more information. + </template> + </template> + </bootstrap-toast> <template v-if="workflowState.workflow"> <div class="row m-1 border-bottom mb-4"> <h1 class="mb-2">Arbitrary Workflow</h1> diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue index a8d84cb7da764c53a97ec862daf979d21b76a14a..d215cdaf972649ad24c42ec59b836ee2a495e640 100644 --- a/src/views/workflows/StartWorkflowView.vue +++ b/src/views/workflows/StartWorkflowView.vue @@ -10,6 +10,7 @@ import { onMounted, ref, reactive, watch } from "vue"; import { useRouter } from "vue-router"; import { Toast } from "bootstrap"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; +import BootstrapToast from "@/components/BootstrapToast.vue"; const executionRepository = useWorkflowExecutionStore(); const props = defineProps<{ @@ -108,34 +109,21 @@ onMounted(() => { </script> <template> - <div class="toast-container position-fixed top-toast end-0 p-3"> - <div - role="alert" - aria-live="assertive" - aria-atomic="true" - class="toast text-bg-danger align-items-center border-0" - data-bs-autohide="true" - id="workflowExecutionViewErrorToast" - > - <div class="d-flex p-2 justify-content-between align-items-center"> - <div class="toast-body"> - <template v-if="versionState.workflowExecutionError === 'limit'"> - You have too many active workflow executions to start a new one. - </template> - <template v-else> - There was an error with starting the workflow execution. Look in the - console for more information. - </template> - </div> - <button - type="button" - class="btn-close btn-close-white" - data-bs-dismiss="toast" - aria-label="Close" - ></button> - </div> - </div> - </div> + <bootstrap-toast + toast-id="workflowExecutionViewErrorToast" + color-class="danger" + > + <template>Error starting workflow</template> + <template #body> + <template v-if="versionState.workflowExecutionError === 'limit'"> + You have too many active workflow executions to start a new one. + </template> + <template v-else> + There was an error with starting the workflow execution. Look in the + console for more information. + </template> + </template> + </bootstrap-toast> <parameter-schema-form-component :workflow-version-id="versionId" :schema="parameterSchema"