From 5e7968af595f942ce90bbe370ed062ac66674b1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Wed, 8 Mar 2023 15:57:56 +0100
Subject: [PATCH] Migrate to font awesome icons adn drop bootstrap icons

#42
---
 package-lock.json                             | 26 ++++++++-------
 package.json                                  |  4 +--
 ...{BootstrapIcon.vue => FontAwesomeIcon.vue} | 19 +++++------
 src/components/NavbarTop.vue                  |  4 +--
 src/components/modals/SearchUserModal.vue     | 12 +++----
 .../object-storage/BucketListItem.vue         | 14 ++++----
 .../object-storage/modals/PermissionModal.vue | 20 +++++------
 src/components/workflows/WorkflowCard.vue     |  4 +--
 .../workflows/WorkflowWithVersionsCard.vue    | 19 ++++++-----
 src/main.ts                                   |  3 ++
 src/views/object-storage/BucketView.vue       | 24 +++++++-------
 src/views/object-storage/BucketsView.vue      | 19 ++++++-----
 src/views/object-storage/S3KeyView.vue        |  6 ++--
 src/views/object-storage/S3KeysView.vue       |  9 +++--
 src/views/workflows/ListWorkflowsView.vue     | 20 ++++++-----
 src/views/workflows/WorkflowView.vue          | 33 ++++++++++++++-----
 16 files changed, 136 insertions(+), 100 deletions(-)
 rename src/components/{BootstrapIcon.vue => FontAwesomeIcon.vue} (58%)

diff --git a/package-lock.json b/package-lock.json
index 950dc82..1daf722 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,9 @@
         "@aws-sdk/client-s3": "^3.281.0",
         "@aws-sdk/lib-storage": "^3.281.0",
         "@aws-sdk/s3-request-presigner": "^3.281.0",
+        "@fortawesome/fontawesome-free": "^6.3.0",
         "@popperjs/core": "^2.11.6",
         "bootstrap": "^5.2.3",
-        "bootstrap-icons": "^1.10.3",
         "dayjs": "^1.11.7",
         "dompurify": "^3.0.1",
         "filesize": "^10.0.6",
@@ -2235,6 +2235,15 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/@fortawesome/fontawesome-free": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz",
+      "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==",
+      "hasInstallScript": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.7",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
@@ -2974,11 +2983,6 @@
         "@popperjs/core": "^2.11.6"
       }
     },
-    "node_modules/bootstrap-icons": {
-      "version": "1.10.3",
-      "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.3.tgz",
-      "integrity": "sha512-7Qvj0j0idEm/DdX9Q0CpxAnJYqBCFCiUI6qzSPYfERMcokVuV9Mdm/AJiVZI8+Gawe4h/l6zFcOzvV7oXCZArw=="
-    },
     "node_modules/bowser": {
       "version": "2.11.0",
       "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
@@ -8589,6 +8593,11 @@
         "strip-json-comments": "^3.1.1"
       }
     },
+    "@fortawesome/fontawesome-free": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz",
+      "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA=="
+    },
     "@humanwhocodes/config-array": {
       "version": "0.11.7",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
@@ -9120,11 +9129,6 @@
       "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
       "requires": {}
     },
-    "bootstrap-icons": {
-      "version": "1.10.3",
-      "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.3.tgz",
-      "integrity": "sha512-7Qvj0j0idEm/DdX9Q0CpxAnJYqBCFCiUI6qzSPYfERMcokVuV9Mdm/AJiVZI8+Gawe4h/l6zFcOzvV7oXCZArw=="
-    },
     "bowser": {
       "version": "2.11.0",
       "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
diff --git a/package.json b/package.json
index 17eaf9f..ca88bc3 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,9 @@
     "@aws-sdk/client-s3": "^3.281.0",
     "@aws-sdk/lib-storage": "^3.281.0",
     "@aws-sdk/s3-request-presigner": "^3.281.0",
+    "@fortawesome/fontawesome-free": "^6.3.0",
     "@popperjs/core": "^2.11.6",
     "bootstrap": "^5.2.3",
-    "bootstrap-icons": "^1.10.3",
     "dayjs": "^1.11.7",
     "dompurify": "^3.0.1",
     "filesize": "^10.0.6",
@@ -33,9 +33,9 @@
     "@esbuild-plugins/node-modules-polyfill": "^0.1.4",
     "@rushstack/eslint-patch": "^1.2.0",
     "@types/bootstrap": "^5.2.6",
+    "@types/dompurify": "^2.4.0",
     "@types/node": "^16.11.45",
     "@types/showdown": "^2.0.0",
-    "@types/dompurify": "^2.4.0",
     "@vitejs/plugin-vue": "^3.2.0",
     "@vue/eslint-config-prettier": "^7.0.0",
     "@vue/eslint-config-typescript": "^11.0.2",
diff --git a/src/components/BootstrapIcon.vue b/src/components/FontAwesomeIcon.vue
similarity index 58%
rename from src/components/BootstrapIcon.vue
rename to src/components/FontAwesomeIcon.vue
index 7c57717..e0a4e3d 100644
--- a/src/components/BootstrapIcon.vue
+++ b/src/components/FontAwesomeIcon.vue
@@ -1,17 +1,16 @@
 <template>
-  <svg
-    class="bi bi-bootstrap-fill"
-    :width="props.width"
-    :height="props.height"
-    :fill="props.fill"
-  >
-    <use v-bind:xlink:href="iconPath + '#' + props.icon" />
-  </svg>
+  <div
+    class="align-middle"
+    :class="icon"
+    :style="{
+      width: props.width,
+      height: props.height,
+      color: props.fill,
+    }"
+  ></div>
 </template>
 
 <script setup lang="ts">
-import iconPath from "bootstrap-icons/bootstrap-icons.svg";
-
 const props = defineProps({
   icon: { type: String, required: true },
   width: { type: String, default: "1em", required: false },
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index 2c68c63..b91aaf0 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { useAuthStore } from "@/stores/auth";
 import { useRoute, useRouter } from "vue-router";
 import { useCookies } from "vue3-cookies";
@@ -150,7 +150,7 @@ watch(
           aria-expanded="false"
         >
           <strong class="me-2">{{ store.user.display_name }}</strong>
-          <bootstrap-icon icon="person-circle" class="fs-4" />
+          <font-awesome-icon icon="fa-solid fa-circle-user" class="fs-5" />
         </a>
         <ul
           class="dropdown-menu dropdown-menu-dark text-small shadow"
diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue
index 9fb35d4..d5b5fcd 100644
--- a/src/components/modals/SearchUserModal.vue
+++ b/src/components/modals/SearchUserModal.vue
@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { reactive, watch } from "vue";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { UserService } from "@/client/auth";
 import type { User } from "@/client/auth";
 import { useAuthStore } from "@/stores/auth";
@@ -82,7 +82,7 @@ function searchUser(name: string) {
     <template v-slot:body>
       <div class="input-group mt-2 mb-4">
         <span class="input-group-text" id="objects-search-wrapping"
-          ><bootstrap-icon icon="search"
+          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
         /></span>
         <input
           class="form-control"
@@ -97,8 +97,8 @@ function searchUser(name: string) {
         </div>
       </div>
       <div v-else-if="formState.error" class="text-center fs-2">
-        <bootstrap-icon
-          icon="x-lg"
+        <font-awesome-icon
+          icon="fa-solid fa-x"
           class="mb-2"
           width="56"
           height="56"
@@ -122,8 +122,8 @@ function searchUser(name: string) {
         </button>
       </div>
       <div v-else class="text-center fs-2">
-        <bootstrap-icon
-          icon="search"
+        <font-awesome-icon
+          icon="fa-solid fa-magnifying-glass"
           class="mb-2"
           width="56"
           height="56"
diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue
index bec5afd..611015d 100644
--- a/src/components/object-storage/BucketListItem.vue
+++ b/src/components/object-storage/BucketListItem.vue
@@ -4,7 +4,7 @@ import type {
   BucketPermissionIn,
   BucketPermissionOut,
 } from "@/client/s3proxy";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue";
 import BucketDetailModal from "@/components/object-storage/modals/BucketDetailModal.vue";
 import dayjs from "dayjs";
@@ -88,18 +88,18 @@ onMounted(() => {
       >
         <span class="text-truncate" style="width: 80%">{{ bucket.name }}</span>
         <div>
-          <bootstrap-icon
+          <font-awesome-icon
             v-if="props.active && permission == null && props.deletable"
-            icon="trash-fill"
+            icon="fa-solid fa-trash"
             class="delete-icon me-2"
             @click="emit('delete-bucket', bucket.name)"
           />
-          <bootstrap-icon
+          <font-awesome-icon
             class="info-icon"
             data-bs-toggle="modal"
             :data-bs-target="'#view-bucket-details-modal' + randomIDSuffix"
             v-if="props.active"
-            icon="info-circle-fill"
+            icon="fa-solid fa-circle-info"
           />
         </div>
       </router-link>
@@ -157,12 +157,12 @@ onMounted(() => {
   color: white;
 }
 .delete-icon:hover {
-  color: var(--bs-danger);
+  color: var(--bs-danger) !important;
 }
 .info-icon {
   color: white;
 }
 .info-icon:hover {
-  color: var(--bs-info);
+  color: var(--bs-info) !important;
 }
 </style>
diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue
index 30048c8..82bfd4a 100644
--- a/src/components/object-storage/modals/PermissionModal.vue
+++ b/src/components/object-storage/modals/PermissionModal.vue
@@ -15,7 +15,7 @@ import type { FolderTree } from "@/types/PseudoFolder";
 import type { ComputedRef, Ref } from "vue";
 import { Permission, BucketPermissionService } from "@/client/s3proxy";
 import { Toast } from "bootstrap";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 
 // Props
 // -----------------------------------------------------------------------------
@@ -333,17 +333,17 @@ onMounted(() => {
     </template>
     <template v-slot:header v-else> Create new Permission </template>
     <template v-slot:extra-button>
-      <bootstrap-icon
+      <font-awesome-icon
         v-if="props.deletable"
-        icon="trash-fill"
+        icon="fa-solid fa-trash"
         class="me-2 cursor-pointer"
         :class="{ 'delete-icon': !formState.loading }"
         data-bs-toggle="modal"
         :data-bs-target="'#delete-permission-modal' + randomIDSuffix"
       />
-      <bootstrap-icon
+      <font-awesome-icon
         v-if="formState.readonly && props.editable"
-        icon="pencil-fill"
+        icon="fa-solid fa-pen"
         class="pseudo-link cursor-pointer"
         @click="formState.readonly = false"
       />
@@ -395,7 +395,7 @@ onMounted(() => {
               data-bs-toggle="modal"
               :data-bs-target="'#search-user-modal' + randomIDSuffix"
             >
-              <bootstrap-icon icon="search" />
+              <font-awesome-icon icon="fa-solid fa-magnifying-glass" />
             </button>
           </div>
         </div>
@@ -499,7 +499,7 @@ onMounted(() => {
               @click="permission.file_prefix = undefined"
               :hidden="permission.file_prefix == undefined"
             >
-              <bootstrap-icon icon="x-lg" />
+              <font-awesome-icon icon="fa-solid fa-x" />
             </button>
           </div>
         </div>
@@ -550,13 +550,13 @@ onMounted(() => {
   color: var(--bs-secondary);
 }
 .pseudo-link:hover {
-  color: var(--bs-primary);
+  color: var(--bs-link-hover-color);
 }
 
 .delete-icon {
-  color: var(--bs-secondary);
+  color: var(--bs-secondary) !important;
 }
 .delete-icon:hover {
-  color: var(--bs-danger);
+  color: var(--bs-danger) !important;
 }
 </style>
diff --git a/src/components/workflows/WorkflowCard.vue b/src/components/workflows/WorkflowCard.vue
index 8c16d99..030081c 100644
--- a/src/components/workflows/WorkflowCard.vue
+++ b/src/components/workflows/WorkflowCard.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import type { WorkflowOut, WorkflowVersionReduced } from "@/client/workflow";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 import { onMounted, ref, computed } from "vue";
 import type { Ref, ComputedRef } from "vue";
@@ -84,7 +84,7 @@ onMounted(() => {
           class="btn btn-outline-success"
           role="button"
         >
-          <bootstrap-icon class="fs-5" icon="tag-fill" />
+          <font-awesome-icon class="fs-5" icon="fa-solid fa-tag" />
           {{ latestVersion?.version }}
         </a>
         <div v-if="props.loading" class="placeholder-glow w-25">
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index bd423d0..cd39933 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -3,7 +3,7 @@ import type { WorkflowOut } from "@/client/workflow";
 import { ref } from "vue";
 import type { Ref } from "vue";
 import { Status } from "@/client/workflow";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 
 const props = defineProps<{
@@ -13,10 +13,10 @@ const props = defineProps<{
 const truncateDescription: Ref<boolean> = ref(true);
 
 const statusToIconMapping: Record<string, string> = {
-  PUBLISHED: "check-circle-fill",
-  DENIED: "x-lg",
-  CREATED: "check-circle",
-  DEPRECATED: "archive-fill",
+  PUBLISHED: "fa-solid fa-circle-check",
+  DENIED: "fa-solid fa-x",
+  CREATED: "fa-solid fa-circle-pause",
+  DEPRECATED: "fa-solid fa-box-archive",
 };
 </script>
 
@@ -33,7 +33,7 @@ const statusToIconMapping: Record<string, string> = {
         <button
           type="button"
           class="btn btn-success"
-          :class="{ disabled: props.loading, placeholder: props.loading }"
+          :class="{ disabled: props.loading }"
         >
           Update
         </button>
@@ -71,6 +71,7 @@ const statusToIconMapping: Record<string, string> = {
                   <img
                     v-if="version.icon_url != null"
                     :src="version.icon_url"
+                    alt="Workflow Version Icon"
                   />
                 </td>
                 <th scope="row" class="fw-bold">{{ version.version }}</th>
@@ -82,10 +83,12 @@ const statusToIconMapping: Record<string, string> = {
                     'text-warning': version.status === Status.DEPRECATED,
                   }"
                 >
-                  <bootstrap-icon :icon="statusToIconMapping[version.status]" />
+                  <font-awesome-icon
+                    :icon="statusToIconMapping[version.status]"
+                  />
                   {{ version.status }}
                 </td>
-                <td>{{ dayjs(version.created_at).format("DD.MM.YYYY") }}</td>
+                <td>{{ dayjs(version.created_at).format("D MMMM YYYY") }}</td>
                 <td>
                   <router-link
                     class="w-fit mx-0"
diff --git a/src/main.ts b/src/main.ts
index 91485f9..d7f01cc 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -11,6 +11,9 @@ import "dayjs/locale/en-gb";
 dayjs.extend(relativeTime); // use plugin
 
 import "bootstrap/dist/css/bootstrap.css";
+import "@fortawesome/fontawesome-free/css/fontawesome.css";
+import "@fortawesome/fontawesome-free/css/solid.css";
+import "@fortawesome/fontawesome-free/css/brands.css";
 
 import "./assets/main.css";
 
diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue
index 7bd9e5e..fdb9a36 100644
--- a/src/views/object-storage/BucketView.vue
+++ b/src/views/object-storage/BucketView.vue
@@ -11,7 +11,7 @@ import type {
   S3ObjectWithFolder,
 } from "@/types/PseudoFolder";
 import { ObjectService } from "@/client/s3proxy";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { filesize } from "filesize";
 import dayjs from "dayjs";
 import { Toast, Tooltip } from "bootstrap";
@@ -580,7 +580,7 @@ watch(
     <div class="col-8">
       <div class="input-group mt-2">
         <span class="input-group-text" id="objects-search-wrapping"
-          ><bootstrap-icon icon="search"
+          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
         /></span>
         <input
           type="text"
@@ -603,7 +603,7 @@ watch(
         data-bs-title="Upload Object"
         data-bs-target="#upload-object-modal"
       >
-        <bootstrap-icon icon="upload" fill="white" />
+        <font-awesome-icon icon="fa-solid fa-upload" fill="white" />
         <span class="visually-hidden">Upload Object</span>
       </button>
       <upload-object-modal
@@ -623,7 +623,7 @@ watch(
         data-bs-title="Create Folder"
         data-bs-target="#create-folder-modal"
       >
-        <bootstrap-icon icon="plus-lg" />
+        <font-awesome-icon icon="fa-solid fa-plus" />
         Folder
         <span class="visually-hidden">Add Folder</span>
       </button>
@@ -645,7 +645,7 @@ watch(
         data-bs-title="Create Bucket Permission"
         data-bs-target="#create-permission-modal"
       >
-        <bootstrap-icon icon="person-plus-fill" />
+        <font-awesome-icon icon="fa-solid fa-user-plus" />
         <span class="visually-hidden">Add Bucket Permission</span>
       </button>
       <permission-modal
@@ -672,7 +672,7 @@ watch(
         data-bs-toggle="modal"
         data-bs-target="#permission-list-modal"
       >
-        <bootstrap-icon icon="person-lines-fill" />
+        <font-awesome-icon icon="fa-solid fa-users-line" />
         <span class="visually-hidden">View Bucket Permissions</span>
       </button>
       <permission-list-modal
@@ -691,8 +691,8 @@ watch(
   <div class="pt-3">
     <!-- If bucket not found -->
     <div v-if="objectState.bucketNotFoundError" class="text-center fs-2 mt-5">
-      <bootstrap-icon
-        icon="search"
+      <font-awesome-icon
+        icon="fa-solid fa-magnifying-glass"
         class="mb-3"
         width="64"
         height="64"
@@ -707,8 +707,8 @@ watch(
       v-else-if="objectState.bucketPermissionError"
       class="text-center fs-2 mt-5"
     >
-      <bootstrap-icon
-        icon="folder-x"
+      <font-awesome-icon
+        icon="fa-solid fa-folder-xmark"
         class="mb-3"
         width="64"
         height="64"
@@ -857,7 +857,7 @@ watch(
                       data-bs-target="#delete-object-modal"
                       :disabled="!writableBucket"
                     >
-                      <bootstrap-icon icon="trash-fill" />
+                      <font-awesome-icon icon="fa-solid fa-trash" />
                       <span class="ms-1">Delete</span>
                     </button>
                   </li>
@@ -877,7 +877,7 @@ watch(
                     )
                   "
                 >
-                  <bootstrap-icon icon="trash-fill" class="me-2" />
+                  <font-awesome-icon icon="fa-solid fa-trash" class="me-2" />
                   <span>Delete</span>
                 </button>
               </div>
diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue
index 15fc387..0db316e 100644
--- a/src/views/object-storage/BucketsView.vue
+++ b/src/views/object-storage/BucketsView.vue
@@ -3,7 +3,7 @@ import type { ComputedRef } from "vue";
 import { computed, onMounted, reactive } from "vue";
 import type { BucketOut } from "@/client/s3proxy";
 import { useRoute, useRouter } from "vue-router";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import CreateBucketModal from "@/components/object-storage/modals/CreateBucketModal.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
 import BucketListItem from "@/components/object-storage/BucketListItem.vue";
@@ -83,7 +83,10 @@ onMounted(() => {
           class="btn btn-light"
           @click.stop.prevent="fetchBuckets"
         >
-          <bootstrap-icon icon="arrow-clockwise" class="fs-5" />
+          <font-awesome-icon
+            icon="fa-solid fa-arrow-rotate-right"
+            class="fs-5"
+          />
           <span class="visually-hidden">Refresh Buckets</span>
         </button>
         <button
@@ -93,13 +96,13 @@ onMounted(() => {
           data-bs-toggle="modal"
           data-bs-target="#create-bucket-modal"
         >
-          <bootstrap-icon icon="plus-lg" class="fs-5" />
+          <font-awesome-icon icon="fa-solid fa-plus" class="fs-5" />
           <span class="visually-hidden">Create Bucket</span>
         </button>
       </div>
       <div class="input-group flex-nowrap mt-2">
         <span class="input-group-text" id="buckets-search-wrapping"
-          ><bootstrap-icon icon="search"
+          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
         /></span>
         <input
           type="text"
@@ -128,8 +131,8 @@ onMounted(() => {
             />
           </div>
           <div v-else class="text-center fs-2 mt-5">
-            <bootstrap-icon
-              icon="search"
+            <font-awesome-icon
+              icon="fa-solid fa-magnifying-glass"
               class="mb-2"
               width="56"
               height="56"
@@ -164,8 +167,8 @@ onMounted(() => {
         v-if="router.currentRoute.value.name === 'buckets'"
         class="text-center fs-2 mt-5"
       >
-        <bootstrap-icon
-          icon="hand-index-thumb-fill"
+        <font-awesome-icon
+          icon="fa-solid fa-hand-back-point-up"
           class="mb-5"
           width="64"
           height="64"
diff --git a/src/views/object-storage/S3KeyView.vue b/src/views/object-storage/S3KeyView.vue
index 7985316..7757fb5 100644
--- a/src/views/object-storage/S3KeyView.vue
+++ b/src/views/object-storage/S3KeyView.vue
@@ -2,7 +2,7 @@
 import type { S3Key } from "@/client/s3proxy";
 import type { Ref } from "vue";
 import { ref, watch } from "vue";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
 
 const props = defineProps<{
@@ -59,9 +59,9 @@ function deleteKeyTrigger() {
       :disabled="props.loading"
       @click="visibleSecret = !visibleSecret"
     >
-      <bootstrap-icon
+      <font-awesome-icon
         class="fs-5"
-        :icon="visibleSecret ? 'eye' : 'eye-slash'"
+        :icon="visibleSecret ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash'"
       />
     </button>
   </div>
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 3d0e964..0ccd0f1 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import S3KeyView from "@/views/object-storage/S3KeyView.vue";
-import BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { reactive, onMounted, computed } from "vue";
 import type { ComputedRef } from "vue";
 import type { S3Key } from "@/client/s3proxy";
@@ -118,11 +118,14 @@ onMounted(() => {
           class="btn btn-light"
           @click="refreshKeys(authStore.currentUID)"
         >
-          <bootstrap-icon icon="arrow-clockwise" class="fs-5" />
+          <font-awesome-icon
+            icon="fa-solid fa-arrow-rotate-right"
+            class="fs-5"
+          />
           <span class="visually-hidden">Refresh S3 Keys</span>
         </button>
         <button type="button" class="btn btn-light" @click="createKey">
-          <bootstrap-icon icon="plus-lg" class="fs-5" />
+          <font-awesome-icon icon="fa-solid fa-plus" class="fs-5" />
           <span class="visually-hidden">Create S3 Key</span>
         </button>
       </div>
diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue
index 8ac24cd..f133a3b 100644
--- a/src/views/workflows/ListWorkflowsView.vue
+++ b/src/views/workflows/ListWorkflowsView.vue
@@ -6,7 +6,7 @@ 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 BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 
 const workflowRepository = useWorkflowStore();
 
@@ -72,7 +72,7 @@ onMounted(() => {
     <div class="col-5 me-auto">
       <div class="input-group">
         <span class="input-group-text" id="workflows-search-wrapping"
-          ><bootstrap-icon icon="search"
+          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
         /></span>
         <input
           type="text"
@@ -119,8 +119,12 @@ onMounted(() => {
         >Latest Release</label
       >
     </div>
-    <bootstrap-icon
-      :icon="workflowsState.sortDesc ? 'sort-down' : 'sort-up'"
+    <font-awesome-icon
+      :icon="
+        workflowsState.sortDesc
+          ? 'fa-solid fa-arrow-down-wide-short'
+          : 'fa-solid fa-arrow-up-wide-short'
+      "
       @click="workflowsState.sortDesc = !workflowsState.sortDesc"
       class="fs-4 ms-3 cursor-pointer"
     />
@@ -130,8 +134,8 @@ onMounted(() => {
       v-if="workflowRepository.workflows.length === 0"
       class="text-center fs-2 mt-5"
     >
-      <bootstrap-icon
-        icon="x-lg"
+      <font-awesome-icon
+        icon="fa-solid fa-x"
         class="my-5"
         width="75"
         height="75"
@@ -143,8 +147,8 @@ onMounted(() => {
       v-else-if="processedWorkflows.length === 0"
       class="text-center fs-2 mt-5"
     >
-      <bootstrap-icon
-        icon="search"
+      <font-awesome-icon
+        icon="fa-solid fa-magnifying-glass"
         class="my-5"
         width="75"
         height="75"
diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue
index bf1eaeb..8cd5816 100644
--- a/src/views/workflows/WorkflowView.vue
+++ b/src/views/workflows/WorkflowView.vue
@@ -4,7 +4,7 @@ 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 BootstrapIcon from "@/components/BootstrapIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import dayjs from "dayjs";
 
 const props = defineProps<{
@@ -75,7 +75,7 @@ function updateWorkflow(workflowId: string) {
   WorkflowService.workflowGetWorkflow(workflowId)
     .then((workflow) => {
       workflowState.workflow = workflow;
-      if (!workflowState.initialOpen) {
+      if (!workflowState.initialOpen || route.params.versionId == null) {
         workflowState.activeVersionId =
           workflow.versions[workflow.versions.length - 1].git_commit_hash;
       } else {
@@ -110,6 +110,20 @@ const versionLaunchable: ComputedRef<boolean> = computed(
   () => activeVersion.value?.status == Status.PUBLISHED ?? false
 );
 
+const gitIcon: ComputedRef<string> = computed(() => {
+  let gitProvider = "git";
+  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);
+});
+
 onMounted(() => {
   updateWorkflow(props.workflowId);
 });
@@ -171,12 +185,15 @@ onMounted(() => {
         :class="{ disabled: !versionLaunchable }"
         href="#"
       >
-        <bootstrap-icon icon="rocket-takeoff-fill" class="me-2" />
+        <font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
         <span class="align-middle">Launch {{ activeVersionString }}</span>
       </a>
-      <div class="input-group w-fit position-absolute end-0">
+      <div
+        v-if="latestVersion != null"
+        class="input-group w-fit position-absolute end-0"
+      >
         <span class="input-group-text px-2" id="workflow-version-wrapping"
-          ><bootstrap-icon icon="tags-fill" class="text-secondary"
+          ><font-awesome-icon icon="fa-solid fa-tags" class="text-secondary"
         /></span>
         <select
           class="form-select form-select-sm"
@@ -200,7 +217,7 @@ onMounted(() => {
         target="_blank"
         class="text-secondary text-decoration-none mx-auto w-fit p-0"
       >
-        <bootstrap-icon icon="git" class="me-1" />
+        <font-awesome-icon :icon="gitIcon" class="me-1" />
         <span class="align-middle">
           {{ workflowState.workflow.repository_url }}</span
         ></a
@@ -209,8 +226,8 @@ onMounted(() => {
   </div>
   <router-view v-if="workflowState.loading || workflowState.workflow != null" />
   <div v-else class="text-center fs-1 mt-5">
-    <bootstrap-icon
-      icon="search"
+    <font-awesome-icon
+      icon="fa-solid fa-magnifying-glass"
       class="my-5"
       width="85"
       height="85"
-- 
GitLab