From adcfd48497c3392502a5e91da61415dbe46e5ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Thu, 11 Jan 2024 11:45:24 +0100 Subject: [PATCH] Refactor Bootstrap toast for easier usage #88 --- src/App.vue | 10 ++- src/assets/main.css | 4 -- src/components/BootstrapToast.vue | 60 ++++++++++++++++ src/components/CopyToClipboardIcon.vue | 47 +++--------- .../object-storage/modals/CopyObjectModal.vue | 54 +++----------- .../modals/CreateFolderModal.vue | 54 +++----------- .../object-storage/modals/PermissionModal.vue | 38 +++------- .../modals/UploadObjectModal.vue | 54 +++----------- .../ParameterSchemaFormComponent.vue | 41 ++++------- ...ourceModal.vue => CreateResourceModal.vue} | 4 +- src/components/resources/ResourceCard.vue | 71 +++++++++++++++++-- .../workflows/modals/CreateWorkflowModal.vue | 24 ++----- .../modals/UpdateWorkflowCredentialsModal.vue | 27 ++----- .../workflows/modals/UpdateWorkflowModal.vue | 24 ++----- .../modals/UpdateWorkflowVersionIconModal.vue | 28 ++------ src/views/LoginView.vue | 40 +++-------- src/views/object-storage/BucketView.vue | 26 ++----- src/views/object-storage/S3KeysView.vue | 26 ++----- src/views/resources/MyResourcesView.vue | 5 +- src/views/workflows/ArbitraryWorkflowView.vue | 44 +++++------- src/views/workflows/StartWorkflowView.vue | 44 +++++------- 21 files changed, 275 insertions(+), 450 deletions(-) create mode 100644 src/components/BootstrapToast.vue rename src/components/resources/{createResourceModal.vue => CreateResourceModal.vue} (98%) diff --git a/src/App.vue b/src/App.vue index 3fa22bb..306e0d6 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 24f661e..5197334 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 0000000..cc2f27f --- /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 9d28cba..ca9627e 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 0687aaa..b51e17d 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 cd26d2c..12400a9 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 60ad031..4ce35b4 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 adafe9a..dfa6f4d 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 7546e8b..69dd3cd 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 27dc54c..0f4e66d 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 f3c9140..3aa21f2 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 86bd586..7c24bca 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 43f057f..130f275 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 1b39368..78ca17f 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 ef52929..420dfc3 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 d9e1987..ef5a66c 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 b3bd376..9f43cbc 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 1221806..1735cdb 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 3b100fc..bb6bccc 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 faedb73..e31a338 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 a8d84cb..d215cda 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" -- GitLab