diff --git a/src/components/FontAwesomeIcon.vue b/src/components/FontAwesomeIcon.vue
index e0a4e3d16d54a67456f8e7d3064b6e9fe912799a..6c0400dbe5accfc0550be51c54e8c110596252ba 100644
--- a/src/components/FontAwesomeIcon.vue
+++ b/src/components/FontAwesomeIcon.vue
@@ -3,8 +3,6 @@
     class="align-middle"
     :class="icon"
     :style="{
-      width: props.width,
-      height: props.height,
       color: props.fill,
     }"
   ></div>
@@ -13,8 +11,6 @@
 <script setup lang="ts">
 const props = defineProps({
   icon: { type: String, required: true },
-  width: { type: String, default: "1em", required: false },
-  height: { type: String, default: "1em", required: false },
   fill: { type: String, default: "currentColor", required: false },
 });
 </script>
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index b91aaf0818812c05f5e362bd1bd289642068624f..e878966ba393096c4aec9ef1a8270f6ff56a56f7 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -4,7 +4,6 @@ import { useAuthStore } from "@/stores/auth";
 import { useRoute, useRouter } from "vue-router";
 import { useCookies } from "vue3-cookies";
 import { watch, ref, computed } from "vue";
-import type { ComputedRef } from "vue";
 
 const router = useRouter();
 const store = useAuthStore();
@@ -18,10 +17,10 @@ function logout() {
 }
 
 const activeRoute = ref("");
-const objectStorageActive: ComputedRef<boolean> = computed(
+const objectStorageActive = computed<boolean>(
   () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys"
 );
-const workflowActive: ComputedRef<boolean> = computed(
+const workflowActive = computed<boolean>(
   () => activeRoute.value == "workflows"
 );
 
@@ -63,7 +62,7 @@ watch(
       <router-link class="navbar-brand ms-3" to="/">
         <img
           src="/src/assets/images/denbi.svg"
-          alt=""
+          alt="Denbi Icon"
           width="24"
           height="24"
           class="d-inline-block align-text-top me-2"
@@ -137,6 +136,9 @@ watch(
                   >My Workflows</router-link
                 >
               </li>
+              <li v-if="store.workflowReviewer || store.admin">
+                <a class="dropdown-item" href="#">Reviews</a>
+              </li>
             </ul>
           </li>
         </ul>
diff --git a/src/components/modals/DeleteModal.vue b/src/components/modals/DeleteModal.vue
index 644cebf12fa0594e7792f83bec2da51f25b24a5a..439d0c3e1700839a5707199f5afc17aceb71a33b 100644
--- a/src/components/modals/DeleteModal.vue
+++ b/src/components/modals/DeleteModal.vue
@@ -1,16 +1,15 @@
 <script setup lang="ts">
 import { onMounted, ref } from "vue";
-import type { Ref } from "vue";
 import { Modal } from "bootstrap";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 
 const props = defineProps<{
   modalID: string;
   objectNameDelete: string;
-  backModalId: string | undefined;
+  backModalId?: string;
 }>();
 
-const confirmDelete: Ref<boolean> = ref(false);
+const confirmDelete = ref<boolean>(false);
 const emit = defineEmits<{
   (e: "confirm-delete"): void;
 }>();
diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue
index d5b5fcd691b82c255413f8b0272c92ee0d572758..44c2c8a948ca83b11cd02819e8273a309758dd36 100644
--- a/src/components/modals/SearchUserModal.vue
+++ b/src/components/modals/SearchUserModal.vue
@@ -8,24 +8,24 @@ import { useAuthStore } from "@/stores/auth";
 
 const props = defineProps<{
   modalID: string;
-  backModalId: string | undefined;
+  backModalId?: string;
 }>();
 const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 
 const store = useAuthStore();
 
-const formState = reactive({
-  searchString: "",
-  potentialUsers: [],
-  lastSearchTimerId: null,
-  error: false,
-  loading: false,
-} as {
+const formState = reactive<{
   searchString: string;
   potentialUsers: User[];
   lastSearchTimerId: ReturnType<typeof setTimeout> | null;
   error: boolean;
   loading: boolean;
+}>({
+  searchString: "",
+  potentialUsers: [],
+  lastSearchTimerId: null,
+  error: false,
+  loading: false,
 });
 
 watch(
@@ -100,9 +100,7 @@ function searchUser(name: string) {
         <font-awesome-icon
           icon="fa-solid fa-x"
           class="mb-2"
-          width="56"
-          height="56"
-          style="color: var(--bs-danger)"
+          style="color: var(--bs-danger); font-size: 4em"
         /><br />
         <span class="text-danger"
           >There seems to be an error<br />Try again later</span
@@ -125,9 +123,7 @@ function searchUser(name: string) {
         <font-awesome-icon
           icon="fa-solid fa-magnifying-glass"
           class="mb-2"
-          width="56"
-          height="56"
-          style="color: var(--bs-secondary)"
+          style="color: var(--bs-secondary); font-size: 4em"
         /><br />
         <span v-if="formState.searchString.length > 2"
           >Could not find any Users</span
diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue
index 611015d8334f219a0d8427d2fa74ef3f062dd88a..00c40364c0d400a1e9dab89fbe61ea8aeb3ef751 100644
--- a/src/components/object-storage/BucketListItem.vue
+++ b/src/components/object-storage/BucketListItem.vue
@@ -10,7 +10,6 @@ import BucketDetailModal from "@/components/object-storage/modals/BucketDetailMo
 import dayjs from "dayjs";
 import { filesize } from "filesize";
 import { computed, onMounted } from "vue";
-import type { ComputedRef } from "vue";
 import { Tooltip } from "bootstrap";
 import { useBucketStore } from "@/stores/buckets";
 import { useRouter } from "vue-router";
@@ -26,7 +25,7 @@ const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 const permissionRepository = useBucketStore();
 const router = useRouter();
 
-const permission: ComputedRef<BucketPermissionOut | undefined> = computed(() =>
+const permission = computed<BucketPermissionOut | undefined>(() =>
   permissionRepository.getBucketPermission(props.bucket.name)
 );
 
diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue
index 09fb71ecb3a27d1db8979495e1452005099c7532..b7221031f678f5dcbebdd215a4c4341900a6dcdc 100644
--- a/src/components/object-storage/modals/CopyObjectModal.vue
+++ b/src/components/object-storage/modals/CopyObjectModal.vue
@@ -14,14 +14,14 @@ const props = defineProps<{
   s3Client: S3Client;
 }>();
 
-const formState = reactive({
-  destKey: "",
-  destBucket: "",
-  uploading: false,
-} as {
+const formState = reactive<{
   destKey: string;
   destBucket: string;
   uploading: boolean;
+}>({
+  destKey: "",
+  destBucket: "",
+  uploading: false,
 });
 const bucketRepository = useBucketStore();
 
diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue
index b1dd71a7fe8f5f5b46b57531079c1d25e83c7347..83b0f554de64265e8785fbb4b9eb09a7fe7dde6c 100644
--- a/src/components/object-storage/modals/CreateBucketModal.vue
+++ b/src/components/object-storage/modals/CreateBucketModal.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import type { BucketIn } from "@/client/s3proxy";
-import { reactive, onMounted } from "vue";
+import { reactive, onMounted, computed, ref } from "vue";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { useRouter } from "vue-router";
 import { Modal } from "bootstrap";
@@ -8,15 +8,16 @@ import { useBucketStore } from "@/stores/buckets";
 
 const router = useRouter();
 const bucketRepository = useBucketStore();
-const bucket = reactive({ name: "", description: "" } as BucketIn);
-const formState = reactive({
-  validated: false,
-  bucketNameTaken: false,
-  loading: false,
-} as {
+const bucket = reactive<BucketIn>({ name: "", description: "" });
+const bucketCreateForm = ref<HTMLFormElement | undefined>(undefined);
+const formState = reactive<{
   validated: boolean;
   bucketNameTaken: boolean;
   loading: boolean;
+}>({
+  validated: false,
+  bucketNameTaken: false,
+  loading: false,
 });
 
 const props = defineProps<{
@@ -29,14 +30,17 @@ onMounted(() => {
   createBucketModal = new Modal("#" + props.modalID);
 });
 
+const formValid = computed<boolean>(
+  () => bucketCreateForm.value?.checkValidity() ?? false
+);
+
 function createBucket() {
   formState.validated = true;
   // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-  const form = document.getElementById("bucketCreateForm")! as HTMLFormElement;
   formState.bucketNameTaken = false;
   bucket.description = bucket.description.trim();
   bucket.name = bucket.name.trim();
-  if (form.checkValidity()) {
+  if (formValid.value) {
     formState.loading = true;
     bucketRepository.createBucket(
       bucket,
@@ -85,6 +89,7 @@ function modalClosed() {
         id="bucketCreateForm"
         :class="{ 'was-validated': formState.validated }"
         novalidate
+        ref="bucketCreateForm"
       >
         <div class="mb-3">
           <label for="bucketNameInput" class="form-label">Bucket Name</label>
diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue
index ef0cf39a93a721e96c555b354387e03e27a8b917..433053548a88834652e4aae2aeb515b13d05865a 100644
--- a/src/components/object-storage/modals/CreateFolderModal.vue
+++ b/src/components/object-storage/modals/CreateFolderModal.vue
@@ -3,7 +3,6 @@ import type { S3Client } from "@aws-sdk/client-s3";
 import { PutObjectCommand } from "@aws-sdk/client-s3";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { computed, onMounted, reactive } from "vue";
-import type { ComputedRef } from "vue";
 import type { S3ObjectMetaInformation } from "@/client/s3proxy";
 import dayjs from "dayjs";
 import { Modal, Toast } from "bootstrap";
@@ -20,20 +19,18 @@ let uploadModal: Modal | null = null;
 let successToast: Toast | null = null;
 let errorToast: Toast | null = null;
 
-const currentFolders: ComputedRef<string[]> = computed(() =>
-  props.keyPrefix.split("/")
-);
+const currentFolders = computed<string[]>(() => props.keyPrefix.split("/"));
 
 const emit = defineEmits<{
   (e: "folder-created", object: S3ObjectMetaInformation): void;
 }>();
 
-const formState = reactive({
-  folderName: "",
-  uploading: false,
-} as {
+const formState = reactive<{
   folderName: string;
   uploading: boolean;
+}>({
+  folderName: "",
+  uploading: false,
 });
 
 function uploadFolder() {
diff --git a/src/components/object-storage/modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue
index 37b2125381ff18dbe0f4e6bd70e7a2811b1a5e9d..ef67554bd6f4758550666f67eee240db566237de 100644
--- a/src/components/object-storage/modals/PermissionListModal.vue
+++ b/src/components/object-storage/modals/PermissionListModal.vue
@@ -18,7 +18,11 @@ const props = defineProps<{
 
 // Reactive State
 // -----------------------------------------------------------------------------
-const state = reactive({
+const state = reactive<{
+  permissions: BucketPermissionOut[];
+  loading: boolean;
+  currentPermission: BucketPermissionOut;
+}>({
   permissions: [],
   loading: true,
   currentPermission: {
@@ -27,10 +31,6 @@ const state = reactive({
     permission: "READ",
     grantee_display_name: "display_name",
   },
-} as {
-  permissions: BucketPermissionOut[];
-  loading: boolean;
-  currentPermission: BucketPermissionOut;
 });
 
 const randomIDSuffix = Math.random().toString(16).substr(2, 8);
diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue
index 82bfd4a9f3d8ef8eb4ad52ad1d383fed2e9e50cf..00a4663b4832a538dfb97adeb2e61f77c711fd8d 100644
--- a/src/components/object-storage/modals/PermissionModal.vue
+++ b/src/components/object-storage/modals/PermissionModal.vue
@@ -12,7 +12,6 @@ import type {
 } from "@/client/s3proxy";
 import type { User } from "@/client/auth";
 import type { FolderTree } from "@/types/PseudoFolder";
-import type { ComputedRef, Ref } from "vue";
 import { Permission, BucketPermissionService } from "@/client/s3proxy";
 import { Toast } from "bootstrap";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
@@ -23,11 +22,11 @@ const props = defineProps<{
   modalID: string;
   bucketName: string;
   subFolders: FolderTree;
-  editUserPermission: BucketPermissionOut | undefined;
+  editUserPermission?: BucketPermissionOut;
   readonly: boolean;
   editable: boolean;
   deletable: boolean;
-  backModalId: string | undefined;
+  backModalId?: string;
 }>();
 
 // Variables
@@ -38,43 +37,48 @@ let successToast: Toast | null = null;
 
 // Reactive State
 // -----------------------------------------------------------------------------
-const formState = reactive({
-  loading: false,
-  grantee_name: "",
-  error: false,
-  readonly: props.readonly,
-} as {
+const formState = reactive<{
   loading: boolean;
   grantee_name: string;
   error: boolean;
   readonly: boolean;
+}>({
+  loading: false,
+  grantee_name: "",
+  error: false,
+  readonly: props.readonly,
 });
 
-const permission = reactive({
+const permission = reactive<BucketPermissionIn>({
   from_timestamp: undefined,
   to_timestamp: undefined,
   file_prefix: undefined,
   permission: undefined,
   uid: "",
   bucket_name: props.bucketName,
-} as BucketPermissionIn);
+});
 
-const permissionDeleted: Ref<boolean> = ref(false);
+const permissionDeleted = ref<boolean>(false);
+const permissionForm = ref<HTMLFormElement | undefined>(undefined);
 
 // Computes Properties
 // -----------------------------------------------------------------------------
-const editPermission: ComputedRef<boolean> = computed(
+const editPermission = computed<boolean>(
   () => props.editUserPermission != undefined
 );
 
-const possibleSubFolders: ComputedRef<string[]> = computed(() =>
+const possibleSubFolders = computed<string[]>(() =>
   findSubFolders(props.subFolders, [])
 );
 
-const permissionUserReadonly: ComputedRef<boolean> = computed(() => {
+const permissionUserReadonly = computed<boolean>(() => {
   return formState.readonly || editPermission.value;
 });
 
+const formValid = computed<boolean>(
+  () => permissionForm.value?.checkValidity() ?? false
+);
+
 // Watchers
 // -----------------------------------------------------------------------------
 watch(
@@ -122,7 +126,7 @@ function toastHidden() {
  * Check if an input should be visible based on its state
  * @param input Input which visibility should be determined.
  */
-function inputVisible(input: string | undefined): boolean {
+function inputVisible(input?: string): boolean {
   return !formState.readonly || input != undefined;
 }
 
@@ -185,11 +189,7 @@ function findSubFolders(
  */
 function formSubmit() {
   formState.error = false;
-  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-  const form = document.getElementById(
-    "permissionCreateEditForm" + randomIDSuffix
-  )! as HTMLFormElement;
-  if (form.checkValidity()) {
+  if (formValid.value) {
     const tempPermission: BucketPermissionIn = permission;
     if (permission.from_timestamp != null) {
       tempPermission.from_timestamp =
@@ -352,6 +352,7 @@ onMounted(() => {
       <form
         @submit.prevent="formSubmit"
         :id="'permissionCreateEditForm' + randomIDSuffix"
+        ref="permissionForm"
       >
         <div class="mb-3 row">
           <label for="bucketNameInput" class="col-2 col-form-label"
diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue
index 5a4e8c4971321b49f74b4136eeb592ff4f630a38..59b00a5e6e2428678a3c4d04c3b61b29f1db4ccc 100644
--- a/src/components/object-storage/modals/UploadObjectModal.vue
+++ b/src/components/object-storage/modals/UploadObjectModal.vue
@@ -2,8 +2,7 @@
 import type { S3Client } from "@aws-sdk/client-s3";
 import { Upload } from "@aws-sdk/lib-storage";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
-import { computed, onMounted, reactive, watch } from "vue";
-import type { ComputedRef } from "vue";
+import { computed, onMounted, reactive, ref, watch } from "vue";
 import type { S3ObjectMetaInformation } from "@/client/s3proxy";
 import dayjs from "dayjs";
 import { filesize } from "filesize";
@@ -14,17 +13,16 @@ const props = defineProps<{
   bucketName: string;
   keyPrefix: string;
   s3Client: S3Client;
-  editObjectFileName: string | undefined;
+  editObjectFileName?: string;
 }>();
 
 const randomIDSuffix = Math.random().toString(16).substr(2, 8);
+const objectFileInput = ref<HTMLInputElement | undefined>(undefined);
 let uploadModal: Modal | null = null;
 let successToast: Toast | null = null;
 let errorToast: Toast | null = null;
 
-const currentFolders: ComputedRef<string[]> = computed(() =>
-  props.keyPrefix.split("/")
-);
+const currentFolders = computed<string[]>(() => props.keyPrefix.split("/"));
 
 const emit = defineEmits<{
   (e: "object-created", object: S3ObjectMetaInformation): void;
@@ -51,11 +49,11 @@ const formState = reactive({
   uploadTotal: number;
 });
 
-const uploadProgress: ComputedRef<number> = computed(() =>
+const uploadProgress = computed<number>(() =>
   Math.round((100 * formState.uploadDone) / formState.uploadTotal)
 );
 
-const editObject: ComputedRef<boolean> = computed(
+const editObject = computed<boolean>(
   () => props.editObjectFileName !== undefined
 );
 
@@ -97,9 +95,9 @@ async function uploadObject() {
       content_type: formState.file?.type ?? "binary/octet-stream",
     });
     formState.key = "";
-    (
-      document.getElementById("objectFile" + randomIDSuffix) as HTMLInputElement
-    ).value = "";
+    if (objectFileInput.value != undefined) {
+      objectFileInput.value.value = "";
+    }
   } catch (e) {
     console.error(e);
     errorToast?.show();
@@ -108,11 +106,13 @@ async function uploadObject() {
   }
 }
 
-// eslint-disable-next-line
-function fileChange(event: any) {
-  formState.file = event.target.files[0];
-  if (!editObject.value) {
-    formState.key = formState.file.name;
+function fileChange() {
+  if (objectFileInput.value != undefined) {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    formState.file = objectFileInput.value.files![0];
+    if (!editObject.value) {
+      formState.key = formState.file.name;
+    }
   }
 }
 
@@ -212,6 +212,7 @@ onMounted(() => {
                 class="form-control"
                 type="file"
                 :id="'objectFile' + randomIDSuffix"
+                ref="objectFileInput"
                 required
                 @change="fileChange"
               />
diff --git a/src/components/workflows/WorkflowCard.vue b/src/components/workflows/WorkflowCard.vue
index 030081c85a42872ea497f0acf3f219b00e4642a1..1c2ac3a8cce1d74ad09c8ec68a25f5bf698d5e92 100644
--- a/src/components/workflows/WorkflowCard.vue
+++ b/src/components/workflows/WorkflowCard.vue
@@ -3,23 +3,18 @@ import type { WorkflowOut, WorkflowVersionReduced } from "@/client/workflow";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 import { onMounted, ref, computed } from "vue";
-import type { Ref, ComputedRef } from "vue";
 import { Tooltip } from "bootstrap";
-import { useWorkflowStore } from "@/stores/workflows";
+import { latestVersion as calculateLatestVersion } from "@/utils/Workflow";
 
 const props = defineProps<{
   workflow: WorkflowOut;
   loading: boolean;
 }>();
-const workflowRepository = useWorkflowStore();
 
 const randomIDSuffix: string = Math.random().toString(16).substr(2, 8);
-const truncateDescription: Ref<boolean> = ref(true);
-const latestVersion: ComputedRef<WorkflowVersionReduced | undefined> = computed(
-  () =>
-    props.loading
-      ? undefined
-      : workflowRepository.latestVersion(props.workflow.workflow_id)
+const truncateDescription = ref<boolean>(true);
+const latestVersion = computed<WorkflowVersionReduced | undefined>(() =>
+  calculateLatestVersion(props.workflow.versions)
 );
 
 onMounted(() => {
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index cd399336c2bbb72ee164b2de9135a51bf3a3b90e..9ea8df454075e436ae091b7e1514b2e19c7a8ff7 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -1,16 +1,20 @@
 <script setup lang="ts">
 import type { WorkflowOut } from "@/client/workflow";
 import { ref } from "vue";
-import type { Ref } from "vue";
 import { Status } from "@/client/workflow";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
+import { sortedVersions } from "@/utils/Workflow";
 
 const props = defineProps<{
   workflow: WorkflowOut;
   loading: boolean;
 }>();
-const truncateDescription: Ref<boolean> = ref(true);
+const truncateDescription = ref<boolean>(true);
+
+const emit = defineEmits<{
+  (e: "workflow-update-click", workflow: WorkflowOut): void;
+}>();
 
 const statusToIconMapping: Record<string, string> = {
   PUBLISHED: "fa-solid fa-circle-check",
@@ -34,6 +38,9 @@ const statusToIconMapping: Record<string, string> = {
           type="button"
           class="btn btn-success"
           :class="{ disabled: props.loading }"
+          @click="emit('workflow-update-click', props.workflow)"
+          data-bs-toggle="modal"
+          data-bs-target="#updateWorkflowModal"
         >
           Update
         </button>
@@ -62,9 +69,7 @@ const statusToIconMapping: Record<string, string> = {
           <table class="table table-dark table-sm table-hover">
             <tbody>
               <tr
-                v-for="version in [...props.workflow.versions].sort((a, b) =>
-                  dayjs(a.created_at).isBefore(b.created_at) ? 1 : -1
-                )"
+                v-for="version in sortedVersions(props.workflow.versions)"
                 :key="version.git_commit_hash"
               >
                 <td class="w-fit">
diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue
index 9a4a96c54a423a86e7487b7353a08ba3e55f5245..f1a60f51908f14e339360e5d146ca36f47d01c6a 100644
--- a/src/components/workflows/modals/CreateWorkflowModal.vue
+++ b/src/components/workflows/modals/CreateWorkflowModal.vue
@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref } from "vue";
-import type { ComputedRef } from "vue";
 import { Modal, Toast } from "bootstrap";
 import type {
   Body_Workflow_create_workflow,
@@ -9,11 +8,32 @@ import type {
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { ApiError, WorkflowService } from "@/client/workflow";
-import { GitRepository } from "@/utils/GitRepository";
-import valid from "semver/functions/valid";
+import {
+  GitRepository,
+  requiredRepositoryFiles,
+  determineGitIcon,
+} from "@/utils/GitRepository";
+import { valid } from "semver";
 
+// Emitted Events
+// =============================================================================
+const emit = defineEmits<{
+  (e: "workflow-created", workflow: WorkflowOut): void;
+}>();
+
+// Props
+// =============================================================================
+const props = defineProps<{
+  modalID: string;
+}>();
+
+// Bootstrap Elements
+// =============================================================================
 let createWorkflowModal: Modal | null = null;
 let successToast: Toast | null = null;
+
+// HTML Form Elements
+// =============================================================================
 const workflowCreateForm = ref<HTMLFormElement | undefined>(undefined);
 const workflowIconInput = ref<HTMLInputElement | undefined>(undefined);
 const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined);
@@ -22,8 +42,13 @@ const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>(
 );
 const workflowNameElement = ref<HTMLInputElement | undefined>(undefined);
 const workflowRepositoryElement = ref<HTMLInputElement | undefined>(undefined);
+
+// Constants
+// =============================================================================
 const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 
+// Reactive State
+// =============================================================================
 const workflow = reactive<Body_Workflow_create_workflow>({
   icon: undefined,
   name: "",
@@ -33,22 +58,6 @@ const workflow = reactive<Body_Workflow_create_workflow>({
   initial_version: undefined,
 });
 
-const gitIcon: ComputedRef<string> = computed(() => {
-  let gitProvider = "git-alt";
-  if (workflow.repository_url.includes("github")) {
-    gitProvider = "github";
-  } else if (workflow.repository_url.includes("gitlab")) {
-    gitProvider = "gitlab";
-  } else if (workflow.repository_url.includes("bitbucket")) {
-    gitProvider = "bitbucket";
-  }
-  return "fa-brands fa-".concat(gitProvider);
-});
-
-const emit = defineEmits<{
-  (e: "workflow-created", workflow: WorkflowOut): void;
-}>();
-
 const formState = reactive<{
   loading: boolean;
   checkRepoLoading: boolean;
@@ -65,24 +74,33 @@ const formState = reactive<{
   unsupportedRepository: false,
 });
 
-const props = defineProps<{
-  modalID: string;
-}>();
-
-const formValid = computed<boolean>(
-  () => workflowCreateForm.value?.checkValidity() ?? false
+// Computed Properties
+// =============================================================================
+const gitIcon = computed<string>(() =>
+  determineGitIcon(workflow.repository_url)
 );
 
+// Functions
+// =============================================================================
 function modalClosed() {
   formState.validated = false;
+  formState.allowUpload = false;
+  formState.missingFiles = [];
+  formState.unsupportedRepository = false;
+  workflowGitCommitHashElement.value?.setCustomValidity("");
+  workflowRepositoryElement.value?.setCustomValidity("");
+  workflowNameElement.value?.setCustomValidity("");
 }
 
+/**
+ * Create a workflow in the backend.
+ */
 function createWorkflow() {
   formState.validated = true;
   workflow.name = workflow.name.trim();
   workflow.short_description = workflow.short_description.trim();
   workflow.initial_version = workflow.initial_version?.trim();
-  if (formValid.value && formState.allowUpload) {
+  if (workflowCreateForm.value?.checkValidity() && formState.allowUpload) {
     formState.loading = true;
     workflowNameElement.value?.setCustomValidity("");
     workflowGitCommitHashElement.value?.setCustomValidity("");
@@ -109,8 +127,11 @@ function createWorkflow() {
   }
 }
 
+/**
+ * Reset the form to an empty state.
+ */
 function resetForm() {
-  formState.validated = false;
+  modalClosed();
   workflow.icon = undefined;
   workflow.name = "";
   workflow.short_description = "";
@@ -122,13 +143,19 @@ function resetForm() {
   }
 }
 
+/**
+ * Watcher function for the file upload in the form.
+ */
 function iconChanged() {
   workflow.icon = workflowIconInput.value?.files?.[0].slice();
 }
 
+/**
+ * Check the workflow repository for the necessary files.
+ */
 function checkRepository() {
   formState.validated = true;
-  if (formValid.value && !formState.allowUpload) {
+  if (workflowCreateForm.value?.checkValidity() && !formState.allowUpload) {
     formState.unsupportedRepository = false;
     formState.missingFiles = [];
     workflowRepositoryElement.value?.setCustomValidity("");
@@ -139,10 +166,7 @@ function checkRepository() {
         workflow.git_commit_hash
       );
       repo
-        .checkFilesExist(
-          ["main.nf", "CHANGELOG.md", "README.md", "nextflow_schema.json"],
-          true
-        )
+        .checkFilesExist(requiredRepositoryFiles, true)
         .then(() => {
           formState.allowUpload = true;
         })
@@ -161,6 +185,9 @@ function checkRepository() {
   }
 }
 
+/**
+ * Check if the version is a valid semantic version
+ */
 function checkVersionValidity() {
   if (valid(workflow.initial_version) == null) {
     workflowVersionElement.value?.setCustomValidity(
@@ -171,6 +198,8 @@ function checkVersionValidity() {
   }
 }
 
+// Lifecycle Events
+// =============================================================================
 onMounted(() => {
   createWorkflowModal = new Modal("#" + props.modalID);
   successToast = new Toast("#successToast-" + randomIDSuffix);
diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..897a48c87909c3dc8f2ae70617549db37ebf070c
--- /dev/null
+++ b/src/components/workflows/modals/UpdateWorkflowModal.vue
@@ -0,0 +1,376 @@
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref, watch } from "vue";
+import { Modal, Toast } from "bootstrap";
+import type {
+  Body_Workflow_update_workflow,
+  WorkflowOut,
+  WorkflowVersionFull,
+} from "@/client/workflow";
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import type { WorkflowVersionReduced, ApiError } from "@/client/workflow";
+import { WorkflowService } from "@/client/workflow";
+import {
+  GitRepository,
+  requiredRepositoryFiles,
+  determineGitIcon,
+} from "@/utils/GitRepository";
+import { valid, lte, inc } from "semver";
+import { latestVersion as calculateLatestVersion } from "@/utils/Workflow";
+
+// Bootstrap Elements
+// =============================================================================
+let updateWorkflowModal: Modal | null = null;
+let successToast: Toast | null = null;
+
+// Form Elements
+// =============================================================================
+const workflowUpdateForm = ref<HTMLFormElement | undefined>(undefined);
+const workflowIconInputElement = ref<HTMLInputElement | undefined>(undefined);
+const workflowVersionElement = ref<HTMLInputElement | undefined>(undefined);
+const workflowGitCommitHashElement = ref<HTMLInputElement | undefined>(
+  undefined
+);
+const workflowIconElement = ref<HTMLImageElement | undefined>(undefined);
+
+// Constants
+// =============================================================================
+const randomIDSuffix = Math.random().toString(16).substr(2, 8);
+
+// Props
+// =============================================================================
+const props = defineProps<{
+  modalID: string;
+  workflow: WorkflowOut;
+}>();
+
+// Reactive State
+// =============================================================================
+const workflowUpdate = reactive<Body_Workflow_update_workflow>({
+  icon: undefined,
+  version: "",
+  git_commit_hash: "",
+});
+
+const formState = reactive<{
+  validated: boolean;
+  missingFiles: string[];
+  loading: boolean;
+  checkRepoLoading: boolean;
+  allowUpload: boolean;
+}>({
+  loading: false,
+  checkRepoLoading: false,
+  allowUpload: false,
+  validated: false,
+  missingFiles: [],
+});
+
+watch(
+  () => props.workflow,
+  () => {
+    resetForm();
+  }
+);
+
+// Computed Properties
+// =============================================================================
+const latestVersion = computed<WorkflowVersionReduced>(() => {
+  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+  return calculateLatestVersion(props.workflow.versions)!;
+});
+const gitIcon = computed<string>(() =>
+  determineGitIcon(props.workflow.repository_url)
+);
+
+const showIcon = computed<boolean>(
+  () =>
+    latestVersion.value.icon_url != undefined ||
+    workflowUpdate.icon != undefined
+);
+
+// Emitted Events
+// =============================================================================
+const emit = defineEmits<{
+  (e: "workflow-updated", workflow: WorkflowVersionFull): void;
+}>();
+
+// Functions
+// =============================================================================
+function iconChanged() {
+  workflowUpdate.icon = workflowIconInputElement.value?.files?.[0].slice();
+  if (workflowUpdate.icon != undefined) {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    workflowIconElement.value!.src = URL.createObjectURL(
+      workflowUpdate.icon.slice()
+    );
+  } else {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    workflowIconElement.value!.src = latestVersion.value.icon_url ?? "";
+  }
+}
+
+function modalClosed() {
+  formState.validated = false;
+  formState.missingFiles = [];
+  formState.allowUpload = false;
+  workflowGitCommitHashElement.value?.setCustomValidity("");
+}
+
+function checkVersionValidity() {
+  if (valid(workflowUpdate.version) == null) {
+    workflowVersionElement.value?.setCustomValidity(
+      "Please use semantic versioning"
+    );
+  } else if (lte(workflowUpdate.version, latestVersion.value.version)) {
+    workflowVersionElement.value?.setCustomValidity(
+      "The new version must be greater than previous version"
+    );
+  } else {
+    workflowVersionElement.value?.setCustomValidity("");
+  }
+}
+
+function checkRepository() {
+  formState.validated = true;
+  if (workflowUpdateForm.value?.checkValidity() && !formState.allowUpload) {
+    formState.missingFiles = [];
+    workflowGitCommitHashElement.value?.setCustomValidity("");
+    const repo = GitRepository.buildRepository(
+      props.workflow.repository_url,
+      workflowUpdate.git_commit_hash
+    );
+    repo
+      .checkFilesExist(requiredRepositoryFiles, true)
+      .then(() => {
+        formState.allowUpload = true;
+      })
+      .catch((e: Error) => {
+        formState.missingFiles = e.message.split(",");
+        workflowGitCommitHashElement.value?.setCustomValidity(
+          "Files are missing in the repository"
+        );
+      });
+  }
+}
+
+function resetForm() {
+  modalClosed();
+  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+  workflowIconElement.value!.src = latestVersion.value.icon_url ?? "";
+  workflowUpdate.version = "";
+  workflowUpdate.icon = undefined;
+  workflowUpdate.git_commit_hash = "";
+  if (workflowIconInputElement.value != undefined) {
+    workflowIconInputElement.value.value = "";
+  }
+}
+
+function updateWorkflow() {
+  formState.validated = true;
+  workflowUpdate.version = workflowUpdate.version.trim();
+  if (workflowUpdateForm.value?.checkValidity() && formState.allowUpload) {
+    formState.loading = true;
+    workflowGitCommitHashElement.value?.setCustomValidity("");
+    WorkflowService.workflowUpdateWorkflow(
+      props.workflow.workflow_id,
+      workflowUpdate
+    )
+      .then((version) => {
+        emit("workflow-updated", version);
+        successToast?.show();
+        updateWorkflowModal?.hide();
+        resetForm();
+      })
+      .catch((error: ApiError) => {
+        const errorText = error.body["detail"];
+        if (errorText.startsWith("Workflow with git_commit_hash")) {
+          workflowGitCommitHashElement.value?.setCustomValidity(
+            "Git commit is already used by a workflow"
+          );
+        }
+      })
+      .finally(() => {
+        formState.loading = false;
+      });
+  }
+}
+
+// Lifecycle Events
+// =============================================================================
+onMounted(() => {
+  updateWorkflowModal = new Modal("#" + props.modalID);
+  successToast = new Toast("#successToast-" + randomIDSuffix);
+});
+</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-modal
+    :modalID="modalID"
+    :static-backdrop="true"
+    modal-label="Update Workflow Modal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template v-slot:header>
+      Update Workflow
+      <span class="fw-bold">{{ props.workflow.name }}</span>
+    </template>
+    <template v-slot:body>
+      <form
+        id="workflowUpdateForm"
+        :class="{ 'was-validated': formState.validated }"
+        ref="workflowUpdateForm"
+      >
+        <div class="mb-3">
+          <span class="me-3">Git Repository URL:</span>
+          <font-awesome-icon :icon="gitIcon" />
+          <a
+            class="ms-2"
+            :href="props.workflow.repository_url"
+            target="_blank"
+            >{{ props.workflow.repository_url }}</a
+          >
+          <img
+            :src="latestVersion.icon_url"
+            ref="workflowIconElement"
+            class="float-end"
+            :hidden="!showIcon"
+          />
+        </div>
+        <div class="mb-3">
+          <label for="workflowGitCommitInput" class="form-label"
+            >Git Commit Hash</label
+          >
+          <div class="input-group">
+            <div class="input-group-text">
+              <font-awesome-icon icon="fa-solid fa-code-commit" />
+            </div>
+            <input
+              type="text"
+              class="form-control text-lowercase"
+              id="workflowGitCommitInput"
+              placeholder="ba8bcd9..."
+              required
+              ref="workflowGitCommitHashElement"
+              maxlength="40"
+              pattern="[0-9a-f]{40}"
+              v-model="workflowUpdate.git_commit_hash"
+              @change="formState.allowUpload = false"
+            />
+          </div>
+        </div>
+        <div v-if="formState.missingFiles.length > 0" class="text-danger">
+          The following files are missing in the repository
+          <ul>
+            <li v-for="file in formState.missingFiles" :key="file">
+              {{ file }}
+            </li>
+          </ul>
+        </div>
+        <div class="row mb-3">
+          <div class="col-4">
+            <label for="workflowVersionInput" class="form-label">Version</label>
+            <div class="input-group">
+              <div class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-tag" />
+              </div>
+              <input
+                type="text"
+                class="form-control"
+                id="workflowRepositoryInput"
+                :placeholder="inc(latestVersion.version, 'patch') ?? undefined"
+                maxlength="10"
+                required
+                ref="workflowVersionElement"
+                @change="checkVersionValidity"
+                v-model="workflowUpdate.version"
+                aria-describedby="versionHelp"
+              />
+            </div>
+            <div id="versionHelp" class="form-text">
+              Previous Version: {{ latestVersion.version }}
+            </div>
+          </div>
+          <div class="col-8">
+            <label for="workflowIconInput" class="form-label"
+              >Optional Icon</label
+            >
+            <input
+              type="file"
+              ref="workflowIconInputElement"
+              accept="image/*"
+              class="form-control"
+              id="workflowIconInput"
+              @change="iconChanged"
+              aria-describedby="iconHelp"
+            />
+            <div id="iconHelp" class="form-text">
+              If not set, the previous icon will be used
+            </div>
+          </div>
+        </div>
+      </form>
+    </template>
+    <template v-slot:footer>
+      <button
+        type="button"
+        class="btn btn-info me-auto"
+        @click="checkRepository"
+        :disabled="formState.allowUpload"
+      >
+        <span
+          v-if="formState.checkRepoLoading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Check Repository
+      </button>
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+      <button
+        type="submit"
+        form="workflowUpdateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading || !formState.allowUpload"
+        @click.prevent="updateWorkflow"
+      >
+        <span
+          v-if="formState.loading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Save
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped>
+img {
+  max-height: 32px;
+  max-width: 32px;
+}
+</style>
diff --git a/src/stores/workflows.ts b/src/stores/workflows.ts
deleted file mode 100644
index 9a2df34df16bf4a2e52c73df7386642a977d5253..0000000000000000000000000000000000000000
--- a/src/stores/workflows.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { defineStore } from "pinia";
-import { WorkflowService } from "@/client/workflow";
-import type { WorkflowVersionReduced } from "@/client/workflow";
-import type { WorkflowOut } from "@/client/workflow";
-import dayjs from "dayjs";
-
-export const useWorkflowStore = defineStore({
-  id: "workflows",
-  state: () =>
-    ({
-      workflows: [],
-    } as {
-      workflows: WorkflowOut[];
-    }),
-  getters: {
-    latestVersion(): (
-      workflowId: string
-    ) => WorkflowVersionReduced | undefined {
-      return (workflowId) => {
-        const workflow = this.workflows.find(
-          (w) => workflowId == w.workflow_id
-        );
-        if (workflow == null || workflow.versions.length == 0) {
-          return undefined;
-        }
-        const vs = [...workflow.versions];
-        vs.sort((a, b) =>
-          dayjs(a.created_at).isBefore(b.created_at) ? -1 : 1
-        );
-        return vs[vs.length - 1];
-      };
-    },
-  },
-  actions: {
-    fetchWorkflows(
-      onFulfilled:
-        | ((workflows: WorkflowOut[]) => void)
-        | null
-        | undefined = null,
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      onRejected: ((reason: any) => void) | null | undefined = null,
-      onFinally: (() => void) | null | undefined = null
-    ) {
-      WorkflowService.workflowListWorkflows()
-        .then((workflows) => {
-          this.workflows = workflows;
-          onFulfilled?.(workflows);
-        })
-        .catch(onRejected)
-        .finally(onFinally);
-    },
-  },
-});
diff --git a/src/utils/GitRepository.ts b/src/utils/GitRepository.ts
index 04e183394da94378cb1266ff70723436eb549b8b..74d25e0de8d840eec565d5dc25830449c71e7095 100644
--- a/src/utils/GitRepository.ts
+++ b/src/utils/GitRepository.ts
@@ -1,5 +1,26 @@
 import axios from "axios";
 
+export const requiredRepositoryFiles = [
+  "main.nf",
+  "CHANGELOG.md",
+  "README.md",
+  "nextflow_schema.json",
+];
+
+export function determineGitIcon(repo_url?: string): string {
+  let gitProvider = "git-alt";
+  if (repo_url != null) {
+    if (repo_url.includes("github")) {
+      gitProvider = "github";
+    } else if (repo_url.includes("gitlab")) {
+      gitProvider = "gitlab";
+    } else if (repo_url.includes("bitbucket")) {
+      gitProvider = "bitbucket";
+    }
+  }
+  return "fa-brands fa-".concat(gitProvider);
+}
+
 export abstract class GitRepository {
   protected repo: URL;
   protected gitCommitHash: string;
diff --git a/src/utils/Workflow.ts b/src/utils/Workflow.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c37cab3b551cd8cf4b95597d3304910b83587b4c
--- /dev/null
+++ b/src/utils/Workflow.ts
@@ -0,0 +1,40 @@
+import type {
+  WorkflowVersionReduced,
+  WorkflowVersionFull,
+} from "@/client/workflow";
+import dayjs from "dayjs";
+
+export function sortedVersions(
+  versions: WorkflowVersionFull[]
+): WorkflowVersionFull[];
+export function sortedVersions(
+  versions: WorkflowVersionReduced[]
+): WorkflowVersionReduced[];
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function sortedVersions(versions: any[], desc = true): any[] {
+  const vs = [...versions];
+  if (desc) {
+    vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? 1 : -1));
+  } else {
+    vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? -1 : 1));
+  }
+  return vs;
+}
+
+export function latestVersion(
+  versions: WorkflowVersionFull[]
+): WorkflowVersionFull | undefined;
+
+export function latestVersion(
+  versions: WorkflowVersionReduced[]
+): WorkflowVersionReduced | undefined;
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function latestVersion(versions: any[]): any | undefined {
+  if (versions == undefined || versions.length == 0) {
+    return undefined;
+  }
+  const vs = sortedVersions(versions);
+  return vs[0];
+}
diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue
index fdb9a366d05a74a29050a73d409b7609961b2481..4ebcf3dd1150a5919b04f26b3b610d1a9e9c25af 100644
--- a/src/views/object-storage/BucketView.vue
+++ b/src/views/object-storage/BucketView.vue
@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { onMounted, reactive, watch, computed } from "vue";
-import type { ComputedRef } from "vue";
 import type {
   S3ObjectMetaInformation,
   BucketPermissionOut,
@@ -91,7 +90,7 @@ authStore.$onAction(({ name, args }) => {
 const props = defineProps<{
   bucketName: string;
   subFolders: string[] | string;
-  permission: BucketPermissionOut | undefined;
+  permission?: BucketPermissionOut;
 }>();
 const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 let successToast: Toast | null = null;
@@ -99,17 +98,27 @@ let successToast: Toast | null = null;
 // Reactive State
 // -----------------------------------------------------------------------------
 
-const deleteObjectsState = reactive({
-  deletedItem: "",
-  potentialObjectToDelete: "",
-  deleteFolder: true,
-} as {
+const deleteObjectsState = reactive<{
   deletedItem: string;
   potentialObjectToDelete: string;
   deleteFolder: boolean;
+}>({
+  deletedItem: "",
+  potentialObjectToDelete: "",
+  deleteFolder: true,
 });
 
-const objectState = reactive({
+const objectState = reactive<{
+  objects: S3ObjectMetaInformation[];
+  loading: boolean;
+  filterString: string;
+  bucketNotFoundError: boolean;
+  bucketPermissionError: boolean;
+  createdPermission: undefined | BucketPermissionOut;
+  editObjectKey: string;
+  copyObject: S3ObjectMetaInformation;
+  viewDetailObject: S3ObjectMetaInformation;
+}>({
   objects: [],
   loading: true,
   filterString: "",
@@ -131,16 +140,6 @@ const objectState = reactive({
     last_modified: "2022-01-01",
     content_type: "text/plain",
   },
-} as {
-  objects: S3ObjectMetaInformation[];
-  loading: boolean;
-  filterString: string;
-  bucketNotFoundError: boolean;
-  bucketPermissionError: boolean;
-  createdPermission: undefined | BucketPermissionOut;
-  editObjectKey: string;
-  copyObject: S3ObjectMetaInformation;
-  viewDetailObject: S3ObjectMetaInformation;
 });
 
 // Watcher
@@ -158,16 +157,17 @@ watch(
 
 // Computed Properties
 // -----------------------------------------------------------------------------
-const filteredObjects: ComputedRef<(S3ObjectWithFolder | S3PseudoFolder)[]> =
-  computed(() => {
+const filteredObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(
+  () => {
     return objectState.filterString.length > 0
       ? visibleObjects.value.filter((obj) =>
           obj.key.includes(objectState.filterString)
         )
       : visibleObjects.value;
-  });
+  }
+);
 
-const folderStructure: ComputedRef<FolderTree> = computed(() => {
+const folderStructure = computed<FolderTree>(() => {
   /**
    * Store the entire folder structure in a bucket in a tree-like data structure
    */
@@ -209,7 +209,7 @@ const folderStructure: ComputedRef<FolderTree> = computed(() => {
   );
 });
 
-const objectsWithFolders: ComputedRef<S3ObjectWithFolder[]> = computed(() => {
+const objectsWithFolders = computed<S3ObjectWithFolder[]>(() => {
   /**
    * Add to the meta information from objects the pseudo filename and their pseudo folder
    * This can be inferred from the key of the object where the '/' character is the delimiter, e.g.
@@ -227,7 +227,7 @@ const objectsWithFolders: ComputedRef<S3ObjectWithFolder[]> = computed(() => {
   });
 });
 
-const currentSubFolders: ComputedRef<string[]> = computed(() => {
+const currentSubFolders = computed<string[]>(() => {
   /**
    * Transform a single sub folder from a string to an array containing the string and
    * replace an empty string with an empty list
@@ -239,54 +239,53 @@ const currentSubFolders: ComputedRef<string[]> = computed(() => {
     : [];
 });
 
-const visibleObjects: ComputedRef<(S3ObjectWithFolder | S3PseudoFolder)[]> =
-  computed(() => {
-    /**
-     * Compute the visible objects based on the current sub folder
-     */
-    let currentFolder = folderStructure.value;
-    // Navigate into right sub folder
-    for (const subFolder of currentSubFolders.value) {
-      if (currentFolder.subFolders[subFolder] == null) {
-        // If sub folder doesn't exist, no object is visible
-        return [];
-      } else {
-        currentFolder = currentFolder.subFolders[subFolder];
-      }
+const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => {
+  /**
+   * Compute the visible objects based on the current sub folder
+   */
+  let currentFolder = folderStructure.value;
+  // Navigate into right sub folder
+  for (const subFolder of currentSubFolders.value) {
+    if (currentFolder.subFolders[subFolder] == null) {
+      // If sub folder doesn't exist, no object is visible
+      return [];
+    } else {
+      currentFolder = currentFolder.subFolders[subFolder];
     }
-    // Add all objects and sub folders from the current sub folder as visible object
-    const arr = [];
-    arr.push(...currentFolder.files);
-    arr.push(
-      ...Object.keys(currentFolder.subFolders).map((subFolderName) => {
-        const folderSize = calculateFolderSize(
-          currentFolder.subFolders[subFolderName]
-        );
-        const folderLastModified = dayjs(
-          calculateFolderLastModified(currentFolder.subFolders[subFolderName])
-        ).toISOString();
-        return {
-          name: subFolderName,
-          size: folderSize,
-          key: subFolderName,
-          parentFolder: currentSubFolders.value,
-          last_modified: folderLastModified,
-        } as S3PseudoFolder;
-      })
-    );
-    return arr.filter((obj) => !obj.key.endsWith(".s3keep"));
-  });
+  }
+  // Add all objects and sub folders from the current sub folder as visible object
+  const arr = [];
+  arr.push(...currentFolder.files);
+  arr.push(
+    ...Object.keys(currentFolder.subFolders).map((subFolderName) => {
+      const folderSize = calculateFolderSize(
+        currentFolder.subFolders[subFolderName]
+      );
+      const folderLastModified = dayjs(
+        calculateFolderLastModified(currentFolder.subFolders[subFolderName])
+      ).toISOString();
+      return {
+        name: subFolderName,
+        size: folderSize,
+        key: subFolderName,
+        parentFolder: currentSubFolders.value,
+        last_modified: folderLastModified,
+      } as S3PseudoFolder;
+    })
+  );
+  return arr.filter((obj) => !obj.key.endsWith(".s3keep"));
+});
 
-const subFolderInUrl: ComputedRef<boolean> = computed(
+const subFolderInUrl = computed<boolean>(
   () => currentSubFolders.value.length > 0
 );
-const errorLoadingObjects: ComputedRef<boolean> = computed(
+const errorLoadingObjects = computed<boolean>(
   () => objectState.bucketPermissionError || objectState.bucketNotFoundError
 );
-const writableBucket: ComputedRef<boolean> = computed(() =>
+const writableBucket = computed<boolean>(() =>
   bucketRepository.writableBucket(props.bucketName)
 );
-const readableBucket: ComputedRef<boolean> = computed(() =>
+const readableBucket = computed<boolean>(() =>
   bucketRepository.readableBucket(props.bucketName)
 );
 
@@ -693,9 +692,7 @@ watch(
     <div v-if="objectState.bucketNotFoundError" class="text-center fs-2 mt-5">
       <font-awesome-icon
         icon="fa-solid fa-magnifying-glass"
-        class="mb-3"
-        width="64"
-        height="64"
+        class="mb-3 fs-0"
         style="color: var(--bs-secondary)"
       />
       <p>
@@ -709,9 +706,7 @@ watch(
     >
       <font-awesome-icon
         icon="fa-solid fa-folder-xmark"
-        class="mb-3"
-        width="64"
-        height="64"
+        class="mb-3 fs-0"
         style="color: var(--bs-secondary)"
       />
       <p>You don't have permission for this bucket</p>
diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue
index 0db316eefa73bfd42697942865532ce0404b853f..f62c71472a525e3444ed4a7161fe9c483928f568 100644
--- a/src/views/object-storage/BucketsView.vue
+++ b/src/views/object-storage/BucketsView.vue
@@ -1,5 +1,4 @@
 <script setup lang="ts">
-import type { ComputedRef } from "vue";
 import { computed, onMounted, reactive } from "vue";
 import type { BucketOut } from "@/client/s3proxy";
 import { useRoute, useRouter } from "vue-router";
@@ -16,14 +15,14 @@ const router = useRouter();
 const bucketRepository = useBucketStore();
 const authStore = useAuthStore();
 
-const bucketsState = reactive({
-  filterString: "",
-  potentialDeleteBucketName: "",
-  loading: true,
-} as {
+const bucketsState = reactive<{
   loading: boolean;
   filterString: string;
   potentialDeleteBucketName: string;
+}>({
+  filterString: "",
+  potentialDeleteBucketName: "",
+  loading: true,
 });
 let deleteModal: Modal | null = null;
 
@@ -33,7 +32,7 @@ function fetchBuckets() {
   });
 }
 
-const filteredBuckets: ComputedRef<BucketOut[]> = computed(() => {
+const filteredBuckets = computed<BucketOut[]>(() => {
   return bucketsState.filterString.length > 0
     ? bucketRepository.buckets.filter((bucket) =>
         bucket.name.includes(bucketsState.filterString)
@@ -133,9 +132,7 @@ onMounted(() => {
           <div v-else class="text-center fs-2 mt-5">
             <font-awesome-icon
               icon="fa-solid fa-magnifying-glass"
-              class="mb-2"
-              width="56"
-              height="56"
+              class="mb-2 fs-0"
               style="color: var(--bs-secondary)"
             />
             <br />
@@ -165,16 +162,14 @@ onMounted(() => {
       <router-view></router-view>
       <div
         v-if="router.currentRoute.value.name === 'buckets'"
-        class="text-center fs-2 mt-5"
+        class="text-center mt-5"
       >
         <font-awesome-icon
-          icon="fa-solid fa-hand-back-point-up"
+          icon="fa-solid fa-hand-pointer"
           class="mb-5"
-          width="64"
-          height="64"
-          style="color: var(--bs-secondary)"
+          style="color: var(--bs-secondary); font-size: 5em"
         />
-        <p>Click on a bucket to browse its content</p>
+        <p class="fs-2">Click on a bucket to browse its content</p>
       </div>
     </div>
   </div>
diff --git a/src/views/object-storage/S3KeyView.vue b/src/views/object-storage/S3KeyView.vue
index 7757fb5952eba5de62b67e32778c1b81c6542dcc..ed567a80fa4c3d177a9a3ef1d2729b9635938b01 100644
--- a/src/views/object-storage/S3KeyView.vue
+++ b/src/views/object-storage/S3KeyView.vue
@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import type { S3Key } from "@/client/s3proxy";
-import type { Ref } from "vue";
 import { ref, watch } from "vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
@@ -22,7 +21,7 @@ watch(
   }
 );
 
-const visibleSecret: Ref<boolean> = ref(false);
+const visibleSecret = ref<boolean>(false);
 
 function deleteKeyTrigger() {
   if (props.deletable) {
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 0ccd0f1b21a1b3f979fe4753ed58eafa4093f87e..f7b997e110a351f6520c36b2bf638749f0e11833 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -2,7 +2,6 @@
 import S3KeyView from "@/views/object-storage/S3KeyView.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { reactive, onMounted, computed } from "vue";
-import type { ComputedRef } from "vue";
 import type { S3Key } from "@/client/s3proxy";
 import { S3KeyService } from "@/client/s3proxy";
 import { useAuthStore } from "@/stores/auth";
@@ -18,21 +17,19 @@ authStore.$onAction(({ name, args }) => {
 
 let successToast: Toast | null = null;
 
-const keyState = reactive({
-  keys: [],
-  activeKey: 0,
-  initialLoading: true,
-  deletedKey: "",
-} as {
+const keyState = reactive<{
   keys: S3Key[];
   activeKey: number;
   initialLoading: boolean;
   deletedKey: string;
+}>({
+  keys: [],
+  activeKey: 0,
+  initialLoading: true,
+  deletedKey: "",
 });
 
-const allowKeyDeletion: ComputedRef<boolean> = computed(
-  () => keyState.keys.length > 1
-);
+const allowKeyDeletion = computed<boolean>(() => keyState.keys.length > 1);
 
 function refreshKeys(uid: string) {
   S3KeyService.s3KeyGetUserKeys(uid)
diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue
index f1e0d6ef15a4bd20e26e51234024c058461b07e4..464e75d2f711f4e20cc14d373d9fda2dc6c81f0f 100644
--- a/src/views/workflows/ListWorkflowsView.vue
+++ b/src/views/workflows/ListWorkflowsView.vue
@@ -1,25 +1,24 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive } from "vue";
-import type { ComputedRef } from "vue";
-import { useWorkflowStore } from "@/stores/workflows";
 import type { WorkflowOut } from "@/client/workflow";
 import WorkflowCard from "@/components/workflows/WorkflowCard.vue";
 import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
 import dayjs from "dayjs";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { WorkflowService } from "@/client/workflow";
 
-const workflowRepository = useWorkflowStore();
-
-const workflowsState = reactive({
-  loading: true,
-  filterString: "",
-  sortByAttribute: "name",
-  sortDesc: true,
-} as {
+const workflowsState = reactive<{
   loading: boolean;
   filterString: string;
   sortByAttribute: string;
   sortDesc: boolean;
+  workflows: WorkflowOut[];
+}>({
+  loading: true,
+  filterString: "",
+  sortByAttribute: "name",
+  sortDesc: true,
+  workflows: [],
 });
 
 const filterFunctionMapping: Record<
@@ -47,9 +46,9 @@ function filterWorkflowWithoutVersion(workflow: WorkflowOut): boolean {
   return workflow.versions.length > 0;
 }
 
-const processedWorkflows: ComputedRef<WorkflowOut[]> = computed(() => {
+const processedWorkflows = computed<WorkflowOut[]>(() => {
   return [
-    ...workflowRepository.workflows.filter(
+    ...workflowsState.workflows.filter(
       (workflow) =>
         filterWorkflowByString(workflow) &&
         filterWorkflowWithoutVersion(workflow)
@@ -59,12 +58,18 @@ const processedWorkflows: ComputedRef<WorkflowOut[]> = computed(() => {
   );
 });
 
+function fetchWorkflows() {
+  WorkflowService.workflowListWorkflows()
+    .then((workflows) => {
+      workflowsState.workflows = workflows;
+    })
+    .finally(() => {
+      workflowsState.loading = false;
+    });
+}
+
 onMounted(() => {
-  workflowRepository.fetchWorkflows(
-    null,
-    null,
-    () => (workflowsState.loading = false)
-  );
+  fetchWorkflows();
 });
 </script>
 
@@ -131,19 +136,17 @@ onMounted(() => {
           : 'fa-solid fa-arrow-up-wide-short'
       "
       @click="workflowsState.sortDesc = !workflowsState.sortDesc"
-      class="fs-4 ms-3 cursor-pointer"
+      class="fs-5 ms-3 cursor-pointer"
     />
   </div>
   <div v-if="!workflowsState.loading">
     <div
-      v-if="workflowRepository.workflows.length === 0"
+      v-if="workflowsState.workflows.length === 0"
       class="text-center fs-2 mt-5"
     >
       <font-awesome-icon
         icon="fa-solid fa-x"
-        class="my-5"
-        width="75"
-        height="75"
+        class="my-5 fs-0"
         style="color: var(--bs-secondary)"
       />
       <p>There are no workflows in the system. Please come again later.</p>
@@ -154,9 +157,7 @@ onMounted(() => {
     >
       <font-awesome-icon
         icon="fa-solid fa-magnifying-glass"
-        class="my-5"
-        width="75"
-        height="75"
+        class="my-5 fs-0"
         style="color: var(--bs-secondary)"
       />
       <p>
diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue
index 079aa54799a1d01281e291bb8beb28f50ea99d03..94b8b0b15aa33cf2a4790a342b14d344a1535a16 100644
--- a/src/views/workflows/MyWorkflowsView.vue
+++ b/src/views/workflows/MyWorkflowsView.vue
@@ -1,21 +1,53 @@
 <script setup lang="ts">
 import { onMounted, reactive } from "vue";
+import type { WorkflowOut, WorkflowVersionFull } from "@/client/workflow";
 import { Status, WorkflowService } from "@/client/workflow";
-import type { WorkflowOut } from "@/client/workflow";
 import { useAuthStore } from "@/stores/auth";
 import WorkflowWithVersionsCard from "@/components/workflows/WorkflowWithVersionsCard.vue";
 import CreateWorkflowModal from "@/components/workflows/modals/CreateWorkflowModal.vue";
 import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
+import UpdateWorkflowModal from "@/components/workflows/modals/UpdateWorkflowModal.vue";
 
 const userRepository = useAuthStore();
 const workflowsState = reactive<{
   workflows: WorkflowOut[];
   loading: boolean;
+  updateWorkflow: WorkflowOut;
 }>({
   workflows: [],
   loading: true,
+  updateWorkflow: {
+    short_description: "",
+    name: "",
+    versions: [
+      {
+        version: "1.0.0",
+        created_at: "01.01.2023",
+        git_commit_hash: "",
+        status: Status.CREATED,
+      },
+    ],
+    repository_url: "",
+    workflow_id: "",
+  },
 });
 
+function workflowUpdateClick(workflow: WorkflowOut) {
+  workflowsState.updateWorkflow = workflow;
+}
+
+function workflowUpdated(version: WorkflowVersionFull) {
+  workflowsState.workflows
+    .find((w) => w.workflow_id == version.workflow_id)
+    ?.versions.push({
+      status: version.status,
+      git_commit_hash: version.git_commit_hash,
+      icon_url: version.icon_url,
+      created_at: version.created_at,
+      version: version.version,
+    });
+}
+
 onMounted(() => {
   WorkflowService.workflowListWorkflows(
     undefined,
@@ -36,6 +68,11 @@ onMounted(() => {
     modal-i-d="createWorkflowModal"
     @workflow-created="(w) => workflowsState.workflows.push(w)"
   />
+  <update-workflow-modal
+    :workflow="workflowsState.updateWorkflow"
+    modal-i-d="updateWorkflowModal"
+    @workflow-updated="workflowUpdated"
+  />
   <div class="d-flex justify-content-between align-items-center mt-5">
     <div class="fs-1 w-fit">My Workflows</div>
     <button
@@ -46,17 +83,26 @@ onMounted(() => {
       Create
     </button>
   </div>
-  <card-transition-group
-    v-if="!workflowsState.loading"
-    class="d-flex flex-wrap align-items-center justify-content-between mt-5"
-  >
-    <workflow-with-versions-card
-      v-for="workflow in workflowsState.workflows"
-      :key="workflow.workflow_id"
-      :workflow="workflow"
-      :loading="false"
-    />
-  </card-transition-group>
+  <div v-if="!workflowsState.loading">
+    <card-transition-group
+      v-if="workflowsState.workflows.length > 0"
+      class="d-flex flex-wrap align-items-center justify-content-between mt-5"
+    >
+      <workflow-with-versions-card
+        v-for="workflow in workflowsState.workflows"
+        :key="workflow.workflow_id"
+        :workflow="workflow"
+        :loading="false"
+        @workflow-update-click="workflowUpdateClick"
+      />
+    </card-transition-group>
+    <div v-else class="text-center mt-5 fs-2">
+      There are currently no workflows that you created.<br />
+      <a href="#" data-bs-toggle="modal" data-bs-target="#createWorkflowModal"
+        >Create a new one</a
+      >
+    </div>
+  </div>
   <div
     v-else
     class="d-flex flex-wrap align-items-center justify-content-between mt-5"
diff --git a/src/views/workflows/WorkflowVersionView.vue b/src/views/workflows/WorkflowVersionView.vue
index 63e70256d39e7e6277389851ad95e11de252c5d7..9ffb18d2f806e318300a3410824feb094ac3026a 100644
--- a/src/views/workflows/WorkflowVersionView.vue
+++ b/src/views/workflows/WorkflowVersionView.vue
@@ -11,7 +11,15 @@ const props = defineProps<{
   activeTab: string;
 }>();
 
-const versionState = reactive({
+const versionState = reactive<{
+  loading: boolean;
+  fileLoading: boolean;
+  version: undefined | WorkflowVersionFull;
+  descriptionMarkdown: string;
+  changelogMarkdown: string;
+  errorLoading: boolean;
+  parameterSchema: Record<string, never>;
+}>({
   loading: true,
   fileLoading: true,
   version: undefined,
@@ -19,13 +27,6 @@ const versionState = reactive({
   descriptionMarkdown: "",
   changelogMarkdown: "",
   parameterSchema: {},
-} as {
-  loading: boolean;
-  fileLoading: boolean;
-  version: undefined | WorkflowVersionFull;
-  descriptionMarkdown: string;
-  changelogMarkdown: string;
-  parameterSchema: Record<string, never>;
 });
 
 watch(
@@ -57,10 +58,6 @@ function updateVersion(versionId: string, workflowId: string) {
     });
 }
 
-onMounted(() => {
-  updateVersion(props.versionId, props.workflowId);
-});
-
 function downloadVersionFiles(version: WorkflowVersionFull) {
   versionState.fileLoading = true;
   const descriptionPromise = axios.get(version.readme_url).then((response) => {
@@ -80,6 +77,10 @@ function downloadVersionFiles(version: WorkflowVersionFull) {
     }
   );
 }
+
+onMounted(() => {
+  updateVersion(props.versionId, props.workflowId);
+});
 </script>
 
 <template>
diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue
index a89cc5d7b76560e73aa521a9b6d5d7e781ebc427..f8fd224a7a1d91f5aa3b06ae980460f9124acd1c 100644
--- a/src/views/workflows/WorkflowView.vue
+++ b/src/views/workflows/WorkflowView.vue
@@ -1,31 +1,43 @@
 <script setup lang="ts">
-import type { ComputedRef } from "vue";
 import { computed, onMounted, reactive, watch } from "vue";
 import type { WorkflowOut, WorkflowVersionReduced } from "@/client/workflow";
 import { Status, WorkflowService } from "@/client/workflow";
 import { useRoute, useRouter } from "vue-router";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import dayjs from "dayjs";
-
+import {
+  latestVersion as calculateLatestVersion,
+  sortedVersions,
+} from "@/utils/Workflow";
+import { determineGitIcon } from "@/utils/GitRepository";
+
+// Props
+// =============================================================================
 const props = defineProps<{
   workflowId: string;
-  versionId: string | undefined;
+  versionId?: string;
 }>();
+
+// Constants
+// =============================================================================
 const router = useRouter();
 const route = useRoute();
 
-const workflowState = reactive({
-  loading: true,
-  workflow: undefined,
-  activeVersionId: "",
-  initialOpen: true,
-} as {
+// Reactive State
+// =============================================================================
+const workflowState = reactive<{
   loading: boolean;
   workflow?: WorkflowOut;
   activeVersionId: string;
   initialOpen: boolean;
+}>({
+  loading: true,
+  workflow: undefined,
+  activeVersionId: "",
+  initialOpen: true,
 });
 
+// Watchers
+// =============================================================================
 watch(
   () => props.workflowId,
   (newWorkflowId, oldWorkflowId) => {
@@ -55,21 +67,35 @@ watch(
   }
 );
 
-function calculateLatestVersion(
-  versions: WorkflowVersionReduced[] | undefined
-): WorkflowVersionReduced | undefined {
-  if (versions == undefined || versions.length == 0) {
-    return undefined;
-  }
-  const vs = [...versions];
-  vs.sort((a, b) => (dayjs(a.created_at).isBefore(b.created_at) ? -1 : 1));
-  return vs[versions.length - 1];
-}
+// Computed Properties
+// =============================================================================
+const latestVersion = computed<WorkflowVersionReduced | undefined>(() =>
+  calculateLatestVersion(workflowState.workflow?.versions || [])
+);
+const activeVersion = computed<WorkflowVersionReduced | undefined>(() =>
+  workflowState.workflow?.versions.find(
+    (w) => w.git_commit_hash === workflowState.activeVersionId
+  )
+);
+
+const activeVersionString = computed<string>(
+  () => activeVersion.value?.version ?? ""
+);
+
+const activeVersionIcon = computed<string | undefined>(
+  () => activeVersion.value?.icon_url
+);
 
-const latestVersion: ComputedRef<WorkflowVersionReduced | undefined> = computed(
-  () => calculateLatestVersion(workflowState.workflow?.versions)
+const versionLaunchable = computed<boolean>(
+  () => activeVersion.value?.status == Status.PUBLISHED ?? false
 );
 
+const gitIcon = computed<string>(() =>
+  determineGitIcon(workflowState.workflow?.repository_url)
+);
+
+// Functions
+// =============================================================================
 function updateWorkflow(workflowId: string) {
   workflowState.loading = true;
   WorkflowService.workflowGetWorkflow(workflowId)
@@ -91,39 +117,8 @@ function updateWorkflow(workflowId: string) {
     });
 }
 
-const activeVersion: ComputedRef<WorkflowVersionReduced | undefined> = computed(
-  () =>
-    workflowState.workflow?.versions.find(
-      (w) => w.git_commit_hash === workflowState.activeVersionId
-    )
-);
-
-const activeVersionString: ComputedRef<string> = computed(
-  () => activeVersion.value?.version ?? ""
-);
-
-const activeVersionIcon: ComputedRef<string | undefined> = computed(
-  () => activeVersion.value?.icon_url
-);
-
-const versionLaunchable: ComputedRef<boolean> = computed(
-  () => activeVersion.value?.status == Status.PUBLISHED ?? false
-);
-
-const gitIcon: ComputedRef<string> = computed(() => {
-  let gitProvider = "git-alt";
-  if (workflowState.workflow !== undefined) {
-    if (workflowState.workflow.repository_url.includes("github")) {
-      gitProvider = "github";
-    } else if (workflowState.workflow.repository_url.includes("gitlab")) {
-      gitProvider = "gitlab";
-    } else if (workflowState.workflow.repository_url.includes("bitbucket")) {
-      gitProvider = "bitbucket";
-    }
-  }
-  return "fa-brands fa-".concat(gitProvider);
-});
-
+// Lifecycle Events
+// =============================================================================
 onMounted(() => {
   updateWorkflow(props.workflowId);
 });
@@ -202,7 +197,7 @@ onMounted(() => {
           v-model="workflowState.activeVersionId"
         >
           <option
-            v-for="version in [...workflowState.workflow.versions].reverse()"
+            v-for="version in sortedVersions(workflowState.workflow?.versions)"
             :key="version.git_commit_hash"
             :value="version.git_commit_hash"
           >
@@ -228,9 +223,7 @@ onMounted(() => {
   <div v-else class="text-center fs-1 mt-5">
     <font-awesome-icon
       icon="fa-solid fa-magnifying-glass"
-      class="my-5"
-      width="85"
-      height="85"
+      class="my-5 fs-0"
       style="color: var(--bs-secondary)"
     />
     <p class="my-5">