This is an automated email from the ASF dual-hosted git repository.

yasith pushed a commit to branch feat/sdk-facade-migration
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git

commit ba5e2f42e294d12c7a970f0e9a4bd444da8afc07
Author: yasithdev <[email protected]>
AuthorDate: Wed Apr 8 00:49:17 2026 -0500

    refactor: migrate Vue 2 to Vue 3, update router and state management
    
    Entry points:
    - Replace `entry((Vue) => { new Vue({...}).$mount() })` pattern with
      `entry(({ createApp }) => { createApp({...}).mount() })`
    - Use `h()` from vue for render functions, pass props directly (not wrapped)
    - Use Vue 3 slot syntax in render functions (object with slot functions)
    
    Router:
    - Replace `new VueRouter({mode: 'history'})` with `createRouter({history: 
createWebHistory()})`
    - Replace catch-all `path: '*'` with `path: '/:pathMatch(.*)*'`
    - Export routes array separately from router.js
    
    State management:
    - Replace `new Vuex.Store()` with `createStore()` from vuex 4
    - Remove `Vue.use(Vuex)` calls (app.use(store) in entry points instead)
    - Update package.json to use vuex ^4.1.0 (pragmatic choice over full pinia 
migration)
    
    Common UI:
    - Update entry.js to pass createApp factory with auto-installed error 
handler
    - Update GlobalErrorHandler to use app.config.errorHandler (not Vue.config)
    - Update VModelMixin: value -> modelValue, $emit('input') -> 
$emit('update:modelValue')
    - Update InputEditorMixin similarly, convert asyncComputed to watch-based 
approach
    - Update MainLayout: $slots.sidebar check to !!$slots.sidebar
    - Remove .native event modifier from 16 Vue files (not needed in Vue 3)
    
    Bootstrap / jQuery removal:
    - Replace jQuery-based tooltip init with Bootstrap 5 JS API in main.js and 
cms.js
    - Remove jQuery and popper.js dependencies
    
    Web components:
    - Remove `import Vue from "vue"` and `Vue.use(BootstrapVue)` from web 
components
    - Update WebComponentInputEditorMixin for Vue 3 compatibility
    - Update web-components/store.js to use createStore()
    - TODO: Full web component migration to defineCustomElement needed
---
 .../django_airavata/apps/admin/package.json        |  2 +-
 .../statistics/ExperimentStatisticsContainer.vue   |  6 +-
 .../IdentityServiceUserManagementContainer.vue     |  2 +-
 .../admin/static/django_airavata_admin/src/main.js | 37 ++++++-----
 .../static/django_airavata_admin/src/router.js     | 10 +--
 .../django_airavata_admin/src/store/index.js       |  9 ++-
 .../django_airavata/apps/auth/package.json         |  2 +-
 .../js/components/UserProfileEditor.vue            |  6 +-
 .../django_airavata_auth/js/entry-user-profile.js  | 18 +++--
 .../static/django_airavata_auth/js/store/index.js  |  9 ++-
 .../js/entry-parser-details.js                     | 26 ++++----
 .../js/parser-edit-entry-point.js                  | 25 +++----
 .../js/parser-listing-entry-point.js               | 14 ++--
 .../js/group-create-entry-point.js                 | 25 +++----
 .../js/group-edit-entry-point.js                   | 27 ++++----
 .../js/group-listing-entry-point.js                | 14 ++--
 .../js/input-editors/InputEditorMixin.js           | 77 ++++++++--------------
 .../django_airavata/apps/workspace/package.json    |  2 +-
 .../input-editors/CheckboxInputEditor.vue          |  2 +-
 .../input-editors/RadioButtonInputEditor.vue       |  2 +-
 .../experiment/input-editors/StringInputEditor.vue |  2 +-
 .../input-editors/TextareaInputEditor.vue          |  2 +-
 .../InteractiveParameterStepperWidget.vue          |  2 +-
 .../InteractiveParameterTextInputWidget.vue        |  2 +-
 .../components/storage/UserStorageCreateView.vue   |  2 +-
 .../js/containers/ExperimentListContainer.vue      |  2 +-
 .../js/entry-create-experiment.js                  | 30 +++++----
 .../js/entry-dashboard.js                          | 30 ++++-----
 .../js/entry-edit-experiment.js                    | 29 ++++----
 .../js/entry-edit-project.js                       | 29 ++++----
 .../js/entry-experiment-list.js                    | 28 ++++----
 .../js/entry-project-list.js                       | 25 +++----
 .../js/entry-user-storage.js                       | 35 +++++-----
 .../js/entry-view-experiment.js                    | 20 +++---
 .../django_airavata_workspace/js/store/index.js    |  9 ++-
 .../js/web-components/ComputeResourceSelector.vue  |  2 +-
 .../ExperimentComputeResourceSelector.vue          |  2 +-
 .../js/web-components/ExperimentEditor.vue         |  4 +-
 .../GroupResourceProfileSelector.vue               |  6 +-
 .../js/web-components/ProjectSelector.vue          |  4 +-
 .../js/web-components/QueueSettingsEditor.vue      | 14 ++--
 .../input-editors/WebComponentInputEditorMixin.js  | 28 ++------
 .../js/web-components/store.js                     |  7 +-
 .../django_airavata/static/common/js/cms.js        | 13 ++--
 .../common/js/components/AutocompleteTextInput.vue |  6 +-
 .../static/common/js/components/MainLayout.vue     |  2 +-
 .../django_airavata/static/common/js/entry.js      | 24 +++----
 .../static/common/js/errors/GlobalErrorHandler.js  | 10 ++-
 .../django_airavata/static/common/js/main.js       | 22 +++----
 .../static/common/js/mixins/VModelMixin.js         | 19 +++---
 .../django_airavata/static/common/js/notices.js    | 40 +++++------
 51 files changed, 374 insertions(+), 391 deletions(-)

diff --git a/airavata-django-portal/django_airavata/apps/admin/package.json 
b/airavata-django-portal/django_airavata/apps/admin/package.json
index ab02e0593..56c956906 100644
--- a/airavata-django-portal/django_airavata/apps/admin/package.json
+++ b/airavata-django-portal/django_airavata/apps/admin/package.json
@@ -24,7 +24,7 @@
     "vuedraggable": "^4.1.0",
     "@vuelidate/core": "^2.0.0",
     "@vuelidate/validators": "^2.0.0",
-    "pinia": "^2.2.0"
+    "vuex": "^4.1.0"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^5.0.0",
diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
index db4628e8a..2d2af69bc 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
@@ -14,7 +14,7 @@
                 <b-form-input
                   v-model.trim="experimentId"
                   placeholder="Experiment ID"
-                  @keydown.native.enter="
+                  @keydown.enter="
                     experimentId && showExperimentDetails(experimentId)
                   "
                 />
@@ -37,7 +37,7 @@
                 <b-form-input
                   v-model.trim="jobId"
                   placeholder="Job ID"
-                  @keydown.native.enter="
+                  @keydown.enter="
                     jobId && showExperimentDetailsForJobId(jobId)
                   "
                 />
@@ -103,7 +103,7 @@
                   <b-form-input
                     v-model="usernameFilter"
                     placeholder="Username"
-                    @keydown.native.enter="loadStatistics"
+                    @keydown.enter="loadStatistics"
                   />
                   <b-input-group-append>
                     <b-button @click="removeUsernameFilter">
diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
index 11a2e24b1..57a91bf93 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
@@ -8,7 +8,7 @@
               <b-form-input
                 v-model="search"
                 placeholder="Search by name, email or username"
-                @keydown.native.enter="searchUsers"
+                @keydown.enter="searchUsers"
               />
               <b-input-group-append>
                 <b-button @click="resetSearch">Reset</b-button>
diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
index 8d0fb8fe1..cc6dbf6cd 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
@@ -1,25 +1,32 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
-import VueResource from "vue-resource";
-import VueRouter from "vue-router";
+import { createRouter, createWebHistory } from "vue-router";
 import VueFlatPickr from "vue-flatpickr-component";
 import App from "./App.vue";
-import router from "./router";
+import { routes } from "./router";
+import createStore from "./store";
 
 import "flatpickr/dist/flatpickr.css";
-import createStore from "./store";
 
-entry((Vue) => {
-  Vue.config.productionTip = false;
+entry(({ createApp }) => {
+  const router = createRouter({
+    history: createWebHistory("/admin/"),
+    routes,
+  });
+
+  const store = createStore();
 
-  Vue.use(VueResource);
-  Vue.use(VueRouter);
-  Vue.use(VueFlatPickr);
+  const app = createApp({
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(App),
+      });
+    },
+  });
 
-  const store = createStore(Vue);
+  app.use(router);
+  app.use(store);
+  app.use(VueFlatPickr);
 
-  new Vue({
-    store,
-    render: (h) => h(components.MainLayout, [h(App)]),
-    router,
-  }).$mount("#app");
+  app.mount("#app");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
index b5e722e97..bb6d3adea 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
@@ -16,9 +16,7 @@ import IdentityServiceUserManagementContainer from 
"./components/users/IdentityS
 import UnverifiedEmailUserManagementContainer from 
"./components/users/UnverifiedEmailUserManagementContainer.vue";
 import UserManagementContainer from 
"./components/users/UserManagementContainer.vue";
 import NoticesManagementContainer from 
"./components/notices/NoticesManagementContainer.vue";
-import VueRouter from "vue-router";
-
-const routes = [
+export const routes = [
   {
     path: "/applications/new",
     component: ApplicationEditorContainer,
@@ -162,8 +160,4 @@ const routes = [
     name: "developers",
   }
 ];
-export default new VueRouter({
-  mode: "history",
-  base: "/admin/",
-  routes: routes,
-});
+export default routes;
diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/store/index.js
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/store/index.js
index 0ee4817ac..0f13350b6 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/store/index.js
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/store/index.js
@@ -1,11 +1,10 @@
-import Vuex from "vuex";
+import { createStore } from "vuex";
 import extendedUserProfile from "./modules/extendedUserProfile";
 
 const debug = process.env.NODE_ENV !== "production";
 
-function createStore(Vue) {
-  Vue.use(Vuex);
-  return new Vuex.Store({
+function makeStore() {
+  return createStore({
     modules: {
       extendedUserProfile,
     },
@@ -13,4 +12,4 @@ function createStore(Vue) {
   });
 }
 
-export default createStore;
+export default makeStore;
diff --git a/airavata-django-portal/django_airavata/apps/auth/package.json 
b/airavata-django-portal/django_airavata/apps/auth/package.json
index 298b9d5c4..6561c383e 100644
--- a/airavata-django-portal/django_airavata/apps/auth/package.json
+++ b/airavata-django-portal/django_airavata/apps/auth/package.json
@@ -18,7 +18,7 @@
     "vue": "^3.4.0",
     "@vuelidate/core": "^2.0.0",
     "@vuelidate/validators": "^2.0.0",
-    "pinia": "^2.2.0"
+    "vuex": "^4.1.0"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^5.0.0",
diff --git 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
index bbac76ab1..cc9f20f36 100644
--- 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
@@ -10,21 +10,21 @@
     <b-form-group label="First Name" :disabled="disabled">
       <b-form-input
         v-model="$v.first_name.$model"
-        @keydown.native.enter="save"
+        @keydown.enter="save"
         :state="validateState($v.first_name)"
       />
     </b-form-group>
     <b-form-group label="Last Name" :disabled="disabled">
       <b-form-input
         v-model="$v.last_name.$model"
-        @keydown.native.enter="save"
+        @keydown.enter="save"
         :state="validateState($v.last_name)"
       />
     </b-form-group>
     <b-form-group label="Email" :disabled="disabled">
       <b-form-input
         v-model="$v.email.$model"
-        @keydown.native.enter="save"
+        @keydown.enter="save"
         :state="validateState($v.email)"
       />
       <b-form-invalid-feedback v-if="!$v.email.email">
diff --git 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/entry-user-profile.js
 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/entry-user-profile.js
index 382bfcd21..17a04a0e5 100644
--- 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/entry-user-profile.js
+++ 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/entry-user-profile.js
@@ -1,11 +1,17 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import UserProfileContainer from "./containers/UserProfileContainer.vue";
 import createStore from "./store";
 
-entry((Vue) => {
-  const store = createStore(Vue);
-  new Vue({
-    store,
-    render: (h) => h(components.MainLayout, [h(UserProfileContainer)]),
-  }).$mount("#user-profile");
+entry(({ createApp }) => {
+  const store = createStore();
+  const app = createApp({
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(UserProfileContainer),
+      });
+    },
+  });
+  app.use(store);
+  app.mount("#user-profile");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/store/index.js
 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/store/index.js
index 8dd262a7c..bdb125868 100644
--- 
a/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/store/index.js
+++ 
b/airavata-django-portal/django_airavata/apps/auth/static/django_airavata_auth/js/store/index.js
@@ -1,12 +1,11 @@
-import Vuex from "vuex";
+import { createStore } from "vuex";
 import userProfile from "./modules/userProfile";
 import extendedUserProfile from "./modules/extendedUserProfile";
 
 const debug = process.env.NODE_ENV !== "production";
 
-function createStore(Vue) {
-  Vue.use(Vuex);
-  return new Vuex.Store({
+function makeStore() {
+  return createStore({
     modules: {
       userProfile,
       extendedUserProfile,
@@ -15,4 +14,4 @@ function createStore(Vue) {
   });
 }
 
-export default createStore;
+export default makeStore;
diff --git 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/entry-parser-details.js
 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/entry-parser-details.js
index 5ef4a4a56..e16886133 100644
--- 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/entry-parser-details.js
+++ 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/entry-parser-details.js
@@ -1,21 +1,12 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import ParserDetailsContainer from "./containers/ParserDetailsContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(ParserDetailsContainer, {
-          props: {
-            parserId: this.parserId,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         parserId: null,
-        launching: false,
       };
     },
     beforeMount() {
@@ -23,5 +14,14 @@ entry((Vue) => {
         this.parserId = this.$el.dataset.parserId;
       }
     },
-  }).$mount("#parser-details");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(ParserDetailsContainer, {
+            parserId: this.parserId,
+          }),
+      });
+    },
+  });
+  app.mount("#parser-details");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-edit-entry-point.js
 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-edit-entry-point.js
index 1c4ed95df..d40bc7093 100644
--- 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-edit-entry-point.js
+++ 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-edit-entry-point.js
@@ -1,17 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import ParserEditContainer from "./containers/ParserEditContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(ParserEditContainer, {
-          props: {
-            parserId: this.parserId,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         parserId: null,
@@ -22,5 +14,14 @@ entry((Vue) => {
         this.parserId = this.$el.dataset.parserId;
       }
     },
-  }).$mount("#edit-parser");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(ParserEditContainer, {
+            parserId: this.parserId,
+          }),
+      });
+    },
+  });
+  app.mount("#edit-parser");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-listing-entry-point.js
 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-listing-entry-point.js
index 63f8d00a8..5d162a41c 100755
--- 
a/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-listing-entry-point.js
+++ 
b/airavata-django-portal/django_airavata/apps/dataparsers/static/django_airavata_dataparsers/js/parser-listing-entry-point.js
@@ -1,8 +1,14 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import ParsersManageContainer from "./containers/ParsersManageContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render: (h) => h(components.MainLayout, [h(ParsersManageContainer)]),
-  }).$mount("#parsers-manage");
+entry(({ createApp }) => {
+  const app = createApp({
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(ParsersManageContainer),
+      });
+    },
+  });
+  app.mount("#parsers-manage");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-create-entry-point.js
 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-create-entry-point.js
index e99116d17..6947c8d53 100644
--- 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-create-entry-point.js
+++ 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-create-entry-point.js
@@ -1,17 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import GroupCreateContainer from "./containers/GroupCreateContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(GroupCreateContainer, {
-          props: {
-            next: this.next,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         next: "/groups/",
@@ -22,5 +14,14 @@ entry((Vue) => {
         this.next = this.$el.dataset.next;
       }
     },
-  }).$mount("#group-create");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(GroupCreateContainer, {
+            next: this.next,
+          }),
+      });
+    },
+  });
+  app.mount("#group-create");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-edit-entry-point.js
 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-edit-entry-point.js
index b01913847..39d87ec81 100644
--- 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-edit-entry-point.js
+++ 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-edit-entry-point.js
@@ -1,18 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import GroupEditContainer from "./containers/GroupEditContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(GroupEditContainer, {
-          props: {
-            groupId: this.groupId,
-            next: this.next,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         groupId: null,
@@ -27,5 +18,15 @@ entry((Vue) => {
         this.next = this.$el.dataset.next;
       }
     },
-  }).$mount("#group-edit");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(GroupEditContainer, {
+            groupId: this.groupId,
+            next: this.next,
+          }),
+      });
+    },
+  });
+  app.mount("#group-edit");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-listing-entry-point.js
 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-listing-entry-point.js
index b818dafc6..fc87eb6e2 100755
--- 
a/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-listing-entry-point.js
+++ 
b/airavata-django-portal/django_airavata/apps/groups/static/django_airavata_groups/js/group-listing-entry-point.js
@@ -1,8 +1,14 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import GroupsManageContainer from "./containers/GroupsManageContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render: (h) => h(components.MainLayout, [h(GroupsManageContainer)]),
-  }).$mount("#group-list");
+entry(({ createApp }) => {
+  const app = createApp({
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(GroupsManageContainer),
+      });
+    },
+  });
+  app.mount("#group-list");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/django-airavata-workspace-plugin-api/js/input-editors/InputEditorMixin.js
 
b/airavata-django-portal/django_airavata/apps/workspace/django-airavata-workspace-plugin-api/js/input-editors/InputEditorMixin.js
index dd2866f14..3af4e8655 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/django-airavata-workspace-plugin-api/js/input-editors/InputEditorMixin.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/django-airavata-workspace-plugin-api/js/input-editors/InputEditorMixin.js
@@ -4,7 +4,7 @@
 import { models } from "django-airavata-api";
 export default {
   props: {
-    value: {
+    modelValue: {
       type: String,
     },
     experimentInput: {
@@ -24,51 +24,19 @@ export default {
       default: false,
     },
   },
+  emits: ["update:modelValue", "valid", "invalid"],
   data() {
     return {
-      data: this.value,
+      data: this.modelValue,
       inputHasBegun: false,
+      // TODO: asyncComputed removed in Vue 3 - these need to be converted to
+      // watch + async methods or composables
+      validationResults: { value: [] },
+      validationMessages: [],
+      valid: false,
+      componentValidState: null,
     };
   },
-  asyncComputed: {
-    validationResults: {
-      get () {
-        let results = this.experimentInput.validate(this.data);
-        let value = []
-        if ("value" in results) {
-          value = Promise.all(results["value"]).then(
-            arr => arr.filter(x => x !== null)
-          )
-        }
-        return {
-          "value": value
-        };
-      },
-      default () {
-        return {
-          "value": []
-        }
-      }
-    },
-    validationMessages: function () {
-      return "value" in this.validationResults
-        ? this.validationResults["value"]
-        : [];
-    },
-    valid: function () {
-      if (this.validationMessages)
-        return this.validationMessages.length === 0;
-      else
-        return false;
-    },
-    componentValidState: function () {
-      if (this.inputHasBegun) {
-        return this.valid;
-      } else {
-        return null;
-      }
-    },
-  },
   computed: {
     editorConfig: function () {
       return this.experimentInput.editorConfig;
@@ -77,7 +45,7 @@ export default {
   methods: {
     valueChanged: function () {
       this.inputHasBegun = true;
-      this.$emit("input", this.data);
+      this.$emit("update:modelValue", this.data);
     },
     checkValidation: function () {
       if (this.valid) {
@@ -86,19 +54,30 @@ export default {
         this.$emit("invalid", this.validationMessages);
       }
     },
+    async updateValidation() {
+      const results = this.experimentInput.validate(this.data);
+      let value = [];
+      if ("value" in results) {
+        value = await Promise.all(results["value"]).then((arr) =>
+          arr.filter((x) => x !== null)
+        );
+      }
+      this.validationResults = { value };
+      this.validationMessages = value;
+      this.valid = this.validationMessages.length === 0;
+      this.componentValidState = this.inputHasBegun ? this.valid : null;
+      this.checkValidation();
+    },
   },
   created: function () {
-    this.checkValidation();
+    this.updateValidation();
   },
   watch: {
-    value(newValue) {
+    modelValue(newValue) {
       this.data = newValue;
     },
-    valid() {
-      this.checkValidation();
+    data() {
+      this.updateValidation();
     },
-    validationMessages() {
-      this.checkValidation();
-    }
   },
 };
diff --git a/airavata-django-portal/django_airavata/apps/workspace/package.json 
b/airavata-django-portal/django_airavata/apps/workspace/package.json
index 12c899212..ca8364386 100644
--- a/airavata-django-portal/django_airavata/apps/workspace/package.json
+++ b/airavata-django-portal/django_airavata/apps/workspace/package.json
@@ -27,7 +27,7 @@
     "vue-flatpickr-component": "^11.0.0",
     "vue-router": "^4.3.0",
     "vue-slider-component": "^4.1.0",
-    "pinia": "^2.2.0"
+    "vuex": "^4.1.0"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^5.0.0",
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/CheckboxInputEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/CheckboxInputEditor.vue
index 1c363de4f..fb0343bff 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/CheckboxInputEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/CheckboxInputEditor.vue
@@ -9,7 +9,7 @@
     :state="componentValidState"
     :disabled="readOnly"
     @input="selectionsChanged"
-    @input.native.stop
+    @input.stop
   />
 </template>
 
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/RadioButtonInputEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/RadioButtonInputEditor.vue
index 3a19b171f..4edbc9cd3 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/RadioButtonInputEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/RadioButtonInputEditor.vue
@@ -9,7 +9,7 @@
     :state="componentValidState"
     :disabled="readOnly"
     @input="valueChanged"
-    @input.native.stop
+    @input.stop
   />
 </template>
 
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/StringInputEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/StringInputEditor.vue
index a272f2c79..2f87e65e1 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/StringInputEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/StringInputEditor.vue
@@ -8,7 +8,7 @@
     :state="componentValidState"
     :disabled="readOnly"
     @input="valueChanged"
-    @input.native.stop
+    @input.stop
   />
 </template>
 
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/TextareaInputEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/TextareaInputEditor.vue
index 8056840ad..ab44590be 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/TextareaInputEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/TextareaInputEditor.vue
@@ -8,7 +8,7 @@
     :disabled="readOnly"
     :state="componentValidState"
     @input="valueChanged"
-    @input.native.stop
+    @input.stop
   />
 </template>
 
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
index 9f61e93ff..6390ca832 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
@@ -8,7 +8,7 @@
       :max="parameter.max"
       :step="parameter.step || 'any'"
       @input="updateValue"
-      @keydown.native.enter="enterKeyPressed"
+      @keydown.enter="enterKeyPressed"
     />
     <b-input-group-append>
       <b-button variant="primary" :disabled="disabled" @click="submit"
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterTextInputWidget.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterTextInputWidget.vue
index 6b9b65d14..c71fec375 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterTextInputWidget.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterTextInputWidget.vue
@@ -4,7 +4,7 @@
       ref="textInput"
       :value="value"
       @input="currentValue = $event"
-      @keydown.native.enter="enterKeyPressed"
+      @keydown.enter="enterKeyPressed"
     />
     <b-input-group-append>
       <b-button variant="primary" :disabled="disabled" @click="submit"
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/storage/UserStorageCreateView.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/storage/UserStorageCreateView.vue
index 7ff525da4..dc2291107 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/storage/UserStorageCreateView.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/storage/UserStorageCreateView.vue
@@ -26,7 +26,7 @@
           <b-form-input
             v-model="dirName"
             placeholder="New directory name"
-            @keydown.native.enter="addDirectory"
+            @keydown.enter="addDirectory"
           ></b-form-input>
           <b-input-group-append>
             <b-button @click="addDirectory" :disabled="!this.dirName"
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
index d2435787f..6ebb268a9 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
@@ -14,7 +14,7 @@
                 v-if="defaultOptionSelected"
                 v-model="search"
                 placeholder="Search Experiments"
-                @keydown.native.enter="searchExperiments"
+                @keydown.enter="searchExperiments"
               />
               <b-form-select
                 v-if="applicationSelected"
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
index 7e3b2b1b4..59bfb098c 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-create-experiment.js
@@ -1,24 +1,15 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import CreateExperimentContainer from 
"./containers/CreateExperimentContainer.vue";
 import "../../scss/styles.scss";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(CreateExperimentContainer, {
-          props: {
-            appModuleId: this.appModuleId,
-            userInputValues: this.userInputValues,
-            experimentDataDir: this.experimentDataDir,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         appModuleId: null,
         userInputValues: null,
+        experimentDataDir: null,
       };
     },
     beforeMount() {
@@ -32,5 +23,16 @@ entry((Vue) => {
         this.experimentDataDir = this.$el.dataset.experimentDataDir;
       }
     },
-  }).$mount("#create-experiment");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(CreateExperimentContainer, {
+            appModuleId: this.appModuleId,
+            userInputValues: this.userInputValues,
+            experimentDataDir: this.experimentDataDir,
+          }),
+      });
+    },
+  });
+  app.mount("#create-experiment");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-dashboard.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-dashboard.js
index 1e04ed688..52812fe3d 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-dashboard.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-dashboard.js
@@ -1,21 +1,10 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import DashboardContainer from "./containers/DashboardContainer.vue";
 import RecentExperimentsContainer from 
"./containers/RecentExperimentsContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(DashboardContainer),
-        h(RecentExperimentsContainer, {
-          props: {
-            viewAllExperiments: this.viewAllExperiments,
-            username: this.username,
-          },
-          slot: "sidebar",
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         viewAllExperiments: null,
@@ -26,5 +15,16 @@ entry((Vue) => {
       this.viewAllExperiments = this.$el.dataset.viewAllExperiments;
       this.username = this.$el.dataset.username;
     },
-  }).$mount("#dashboard");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(DashboardContainer),
+        sidebar: () =>
+          h(RecentExperimentsContainer, {
+            viewAllExperiments: this.viewAllExperiments,
+            username: this.username,
+          }),
+      });
+    },
+  });
+  app.mount("#dashboard");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
index bce1d6b9c..68f1b56b8 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-experiment.js
@@ -1,22 +1,10 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import EditExperimentContainer from "./containers/EditExperimentContainer.vue";
 import "../../scss/styles.scss";
 
-// Expect a template with id "edit-experiment" and experiment-id data attribute
-//
-//   <div id="edit-experiment" data-experiment-id="..expid.."/>
-
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(EditExperimentContainer, {
-          props: {
-            experimentId: this.experimentId,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         experimentId: null,
@@ -25,5 +13,14 @@ entry((Vue) => {
     beforeMount() {
       this.experimentId = this.$el.dataset.experimentId;
     },
-  }).$mount("#edit-experiment");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(EditExperimentContainer, {
+            experimentId: this.experimentId,
+          }),
+      });
+    },
+  });
+  app.mount("#edit-experiment");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-project.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-project.js
index c7bee575a..f6693d369 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-project.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-edit-project.js
@@ -1,21 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import EditProjectContainer from "./containers/EditProjectContainer.vue";
 
-// Expect a template with id "edit-project" and project-id data attribute
-//
-//   <div id="edit-project" data-project-id="..projectID.."/>
-
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(EditProjectContainer, {
-          props: {
-            projectId: this.projectId,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         projectId: null,
@@ -24,5 +12,14 @@ entry((Vue) => {
     beforeMount() {
       this.projectId = this.$el.dataset.projectId;
     },
-  }).$mount("#edit-project");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(EditProjectContainer, {
+            projectId: this.projectId,
+          }),
+      });
+    },
+  });
+  app.mount("#edit-project");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-experiment-list.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-experiment-list.js
index 4c0c32516..1f618ed1a 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-experiment-list.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-experiment-list.js
@@ -1,20 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import ExperimentListContainer from "./containers/ExperimentListContainer.vue";
-import VueFlatPickr from "vue-flatpickr-component";
-import "flatpickr/dist/flatpickr.css";
 
-entry((Vue) => {
-  Vue.use(VueFlatPickr);
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(ExperimentListContainer, {
-          props: {
-            initialExperimentsData: this.experimentsData,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         experimentsData: null,
@@ -25,5 +14,14 @@ entry((Vue) => {
         this.experimentsData = JSON.parse(this.$el.dataset.experimentsData);
       }
     },
-  }).$mount("#experiment-list");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(ExperimentListContainer, {
+            initialExperimentsData: this.experimentsData,
+          }),
+      });
+    },
+  });
+  app.mount("#experiment-list");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-project-list.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-project-list.js
index 26bb41ea5..5bd06b050 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-project-list.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-project-list.js
@@ -1,17 +1,9 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import ProjectListContainer from "./containers/ProjectListContainer.vue";
 
-entry((Vue) => {
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [
-        h(ProjectListContainer, {
-          props: {
-            initialProjectsData: this.projectsData,
-          },
-        }),
-      ]);
-    },
+entry(({ createApp }) => {
+  const app = createApp({
     data() {
       return {
         projectsData: null,
@@ -22,5 +14,14 @@ entry((Vue) => {
         this.projectsData = JSON.parse(this.$el.dataset.projectsData);
       }
     },
-  }).$mount("#project-list");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () =>
+          h(ProjectListContainer, {
+            initialProjectsData: this.projectsData,
+          }),
+      });
+    },
+  });
+  app.mount("#project-list");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-user-storage.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-user-storage.js
index a6bb903c7..cc3266a26 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-user-storage.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-user-storage.js
@@ -1,26 +1,29 @@
-import {components, entry} from "django-airavata-common-ui";
+import { h } from "vue";
+import { components, entry } from "django-airavata-common-ui";
+import { createRouter, createWebHistory } from "vue-router";
 import UserStorageContainer from "./containers/UserStorageContainer.vue";
 import UserStoragePathViewer from 
"./components/storage/UserStoragePathViewer.vue";
 
-import VueRouter from "vue-router";
-
 const routes = [
   {
-    path: "*",
+    path: "/:pathMatch(.*)*",
     component: UserStoragePathViewer,
   },
 ];
-const router = new VueRouter({
-  mode: "history",
-  base: "/workspace/storage",
-  routes: routes,
-});
-entry((Vue) => {
-  Vue.use(VueRouter);
-  new Vue({
-    render(h) {
-      return h(components.MainLayout, [h(UserStorageContainer)]);
+
+entry(({ createApp }) => {
+  const router = createRouter({
+    history: createWebHistory("/workspace/storage"),
+    routes,
+  });
+
+  const app = createApp({
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(UserStorageContainer),
+      });
     },
-    router,
-  }).$mount("#user-storage");
+  });
+  app.use(router);
+  app.mount("#user-storage");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
index 7eb7cffea..ddd652abf 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/entry-view-experiment.js
@@ -1,15 +1,12 @@
+import { h } from "vue";
 import { components, entry } from "django-airavata-common-ui";
 import { mapActions } from "vuex";
 import ExperimentSummary from "./components/experiment/ExperimentSummary.vue";
 import createStore from "./store";
 
-entry((Vue) => {
-  const store = createStore(Vue);
-  new Vue({
-    store,
-    render(h) {
-      return h(components.MainLayout, [h(ExperimentSummary)]);
-    },
+entry(({ createApp }) => {
+  const store = createStore();
+  const app = createApp({
     async beforeMount() {
       const fullExperimentData = JSON.parse(
         this.$el.dataset.fullExperimentData
@@ -26,5 +23,12 @@ entry((Vue) => {
         "setLaunching",
       ]),
     },
-  }).$mount("#view-experiment");
+    render() {
+      return h(components.MainLayout, null, {
+        default: () => h(ExperimentSummary),
+      });
+    },
+  });
+  app.use(store);
+  app.mount("#view-experiment");
 });
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
index e833bd1a1..534cb8ba8 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/store/index.js
@@ -1,11 +1,10 @@
-import Vuex from "vuex";
+import { createStore } from "vuex";
 import viewExperiment from "./modules/view-experiment";
 
 const debug = process.env.NODE_ENV !== "production";
 
-function createStore(Vue) {
-  Vue.use(Vuex);
-  return new Vuex.Store({
+function makeStore() {
+  return createStore({
     modules: {
       viewExperiment,
     },
@@ -13,4 +12,4 @@ function createStore(Vue) {
   });
 }
 
-export default createStore;
+export default makeStore;
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
index b1c52fdb4..0ae5edb44 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ComputeResourceSelector.vue
@@ -6,7 +6,7 @@
       :options="computeResourceOptions"
       required
       @input="computeResourceChanged"
-      @input.native.stop
+      @input.stop
       :disabled="disabled || computeResourceOptions.length === 0"
     >
       <template slot="first">
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
index 7f047775a..30f1b5f70 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentComputeResourceSelector.vue
@@ -4,7 +4,7 @@
       :value="resourceHostId"
       :disabled="disabled"
       :includedComputeResources="computeResources"
-      @input.native.stop="computeResourceChanged"
+      @input.stop="computeResourceChanged"
     />
   </div>
 </template>
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
index c45d9f17c..79a222f94 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ExperimentEditor.vue
@@ -34,12 +34,10 @@
 </template>
 
 <script>
-import Vue from "vue";
+
 import store from "./store";
 import { mapGetters } from "vuex";
-import { BootstrapVue } from "bootstrap-vue";
 import urls from "../utils/urls";
-Vue.use(BootstrapVue);
 
 export default {
   props: {
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
index 3546dc73b..b4e97c15a 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/GroupResourceProfileSelector.vue
@@ -6,7 +6,7 @@
       :options="groupResourceProfileOptions"
       required
       @change="groupResourceProfileChanged"
-      @input.native.stop
+      @input.stop
       :disabled="disabled"
     >
       <template slot="first">
@@ -19,11 +19,9 @@
 </template>
 
 <script>
-import Vue from "vue";
-import { BootstrapVue } from "bootstrap-vue";
+
 import store from "./store";
 import { mapGetters } from "vuex";
-Vue.use(BootstrapVue);
 
 export default {
   name: "group-resource-profile-selector",
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
index 801453ce2..cab84e859 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/ProjectSelector.vue
@@ -27,11 +27,9 @@
 </template>
 
 <script>
-import Vue from "vue";
+
 import store from "./store";
 import { mapGetters } from "vuex";
-import { BootstrapVue } from "bootstrap-vue";
-Vue.use(BootstrapVue);
 
 export default {
   props: {
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
index d8f8e9460..36aa03967 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
@@ -42,7 +42,7 @@
           :options="queueOptions"
           required
           @change="queueChanged"
-          @input.native.stop
+          @input.stop
         >
         </b-form-select>
         <div slot="description">{{ queueDescription }}</div>
@@ -57,7 +57,7 @@
               :max="maxAllowedNodes"
               :value="getNodeCount"
               required
-              @input.native.stop="updateNodeCount"
+              @input.stop="updateNodeCount"
             >
             </b-form-input>
             <div slot="description">
@@ -73,7 +73,7 @@
             :max="maxAllowedCores"
             :value="getTotalCPUCount"
             required
-            @input.native.stop="updateTotalCPUCount"
+            @input.stop="updateTotalCPUCount"
           >
           </b-form-input>
           <div slot="description">
@@ -106,7 +106,7 @@
             :max="maxAllowedWalltime"
             :value="getWallTimeLimit"
             required
-            @input.native.stop="updateWallTimeLimit"
+            @input.stop="updateWallTimeLimit"
           >
           </b-form-input>
         </b-input-group>
@@ -127,7 +127,7 @@
             min="0"
             :max="maxMemory"
             :value="getTotalPhysicalMemory"
-            @input.native.stop="updateTotalPhysicalMemory"
+            @input.stop="updateTotalPhysicalMemory"
           >
           </b-form-input>
         </b-input-group>
@@ -148,11 +148,9 @@
 
 <script>
 import { utils } from "django-airavata-api";
-import Vue from "vue";
+
 import store from "./store";
 import { mapGetters } from "vuex";
-import { BootstrapVue } from "bootstrap-vue";
-Vue.use(BootstrapVue);
 
 export default {
   store: store,
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/input-editors/WebComponentInputEditorMixin.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/input-editors/WebComponentInputEditorMixin.js
index da7bf54f3..ba837c3ed 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/input-editors/WebComponentInputEditorMixin.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/input-editors/WebComponentInputEditorMixin.js
@@ -1,33 +1,18 @@
 
-import Vue from "vue";
-import { BootstrapVue } from "bootstrap-vue";
-import AsyncComputed from "vue-async-computed";
 import { utils } from "django-airavata-common-ui";
 import store from "../store";
-Vue.use(BootstrapVue);
-Vue.use(AsyncComputed);
 
 export default {
   props: {
-    value: String,
+    modelValue: String,
     name: String,
   },
-  store: store,
-  mounted() {
-    this.$nextTick(() => {
-      for (const key of Object.keys(this.$props)) {
-        // workaround for issues around setting props before WC connected,
-        // see https://github.com/vuejs/vue-web-component-wrapper/pull/81
-
-        // copy properties set on host element to wrapper component
-        // (mostly this is done so that the options array can be set by client 
code)
-        this.$parent.props[key] = this.$el.getRootNode().host[key];
-      }
-    })
-  },
+  emits: ["update:modelValue"],
+  // TODO: web components need Vue 3 defineCustomElement migration
+  // store: store,
   data() {
     return {
-      data: this.value,
+      data: this.modelValue,
     };
   },
   computed: {
@@ -45,6 +30,7 @@ export default {
     valueChanged(value) {
       if (value !== this.data) {
         this.data = value;
+        this.$emit("update:modelValue", this.data);
         const inputEvent = new CustomEvent("input", {
           detail: [this.data],
           composed: true,
@@ -55,7 +41,7 @@ export default {
     },
   },
   watch: {
-    value(value) {
+    modelValue(value) {
       this.data = value;
     },
   },
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
index 132249943..971d09f81 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
@@ -1,8 +1,5 @@
 import { errors, services, utils } from "django-airavata-api";
-import Vue from "vue";
-import Vuex from "vuex";
-
-Vue.use(Vuex);
+import { createStore } from "vuex";
 
 const PROMISES = {
   workspacePreferences: null,
@@ -836,7 +833,7 @@ export const getters = {
       : false,
 };
 
-export default new Vuex.Store({
+export default createStore({
   strict: process.env.NODE_ENV !== "production",
   state: {
     experiment: null,
diff --git a/airavata-django-portal/django_airavata/static/common/js/cms.js 
b/airavata-django-portal/django_airavata/static/common/js/cms.js
index 44becdf9d..91eb64aec 100644
--- a/airavata-django-portal/django_airavata/static/common/js/cms.js
+++ b/airavata-django-portal/django_airavata/static/common/js/cms.js
@@ -1,9 +1,14 @@
-import bootstrap from "bootstrap"; // eslint-disable-line no-unused-vars
-import $ from "jquery";
+import * as bootstrap from "bootstrap";
 
 import "bootstrap/dist/css/bootstrap.css";
 import "@fortawesome/fontawesome-free/css/all.css";
 
-$(function () {
-  $('[data-toggle="tooltip"]').tooltip();
+// Initialize Bootstrap tooltips
+document.addEventListener("DOMContentLoaded", () => {
+  const tooltipTriggerList = document.querySelectorAll(
+    '[data-bs-toggle="tooltip"]'
+  );
+  tooltipTriggerList.forEach(
+    (tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
+  );
 });
diff --git 
a/airavata-django-portal/django_airavata/static/common/js/components/AutocompleteTextInput.vue
 
b/airavata-django-portal/django_airavata/static/common/js/components/AutocompleteTextInput.vue
index 18eb10096..9fe760b69 100644
--- 
a/airavata-django-portal/django_airavata/static/common/js/components/AutocompleteTextInput.vue
+++ 
b/airavata-django-portal/django_airavata/static/common/js/components/AutocompleteTextInput.vue
@@ -9,9 +9,9 @@
         :value="searchValue"
         :placeholder="placeholder"
         @input="updateSearchValue"
-        @keydown.native.enter="enter"
-        @keydown.native.down="down"
-        @keydown.native.up="up"
+        @keydown.enter="enter"
+        @keydown.down="down"
+        @keydown.up="up"
       ></b-form-input>
     </b-input-group>
     <b-list-group class="autocomplete-suggestion-list" v-if="open">
diff --git 
a/airavata-django-portal/django_airavata/static/common/js/components/MainLayout.vue
 
b/airavata-django-portal/django_airavata/static/common/js/components/MainLayout.vue
index 081ddeafe..c1c0af9df 100644
--- 
a/airavata-django-portal/django_airavata/static/common/js/components/MainLayout.vue
+++ 
b/airavata-django-portal/django_airavata/static/common/js/components/MainLayout.vue
@@ -20,7 +20,7 @@ export default {
   name: "main-layout",
   computed: {
     hasSidebar() {
-      return this.$slots.sidebar;
+      return !!this.$slots.sidebar;
     },
   },
   components: {
diff --git a/airavata-django-portal/django_airavata/static/common/js/entry.js 
b/airavata-django-portal/django_airavata/static/common/js/entry.js
index a9041c653..5f45d795b 100644
--- a/airavata-django-portal/django_airavata/static/common/js/entry.js
+++ b/airavata-django-portal/django_airavata/static/common/js/entry.js
@@ -1,25 +1,21 @@
-import Vue from "vue";
-import BootstrapVue from "bootstrap-vue";
+import { createApp as vueCreateApp } from "vue";
 import GlobalErrorHandler from "./errors/GlobalErrorHandler";
-import AsyncComputed from "vue-async-computed";
 
 GlobalErrorHandler.init();
 
-// This is imported globally on the website (see main.js) so no need to include
-// it again in this view
-// import 'bootstrap/dist/css/bootstrap.css'
-import "bootstrap-vue/dist/bootstrap-vue.css";
-
 /**
  * Common entry point function. Sets up common entry point functionality and
- * then calls the passed function with the Vue class as the first argument.
+ * then calls the passed function with a createApp factory that auto-installs
+ * the global error handler.
  *
- * @param {Function} entryPointFunction
+ * @param {Function} entryPointFunction - receives { createApp }
  */
 export default function entry(entryPointFunction) {
-  // Common Vue configuration
-  Vue.use(BootstrapVue);
-  Vue.use(AsyncComputed)
+  function createApp(options) {
+    const app = vueCreateApp(options);
+    GlobalErrorHandler.installVueErrorHandler(app);
+    return app;
+  }
 
-  entryPointFunction(Vue);
+  entryPointFunction({ createApp });
 }
diff --git 
a/airavata-django-portal/django_airavata/static/common/js/errors/GlobalErrorHandler.js
 
b/airavata-django-portal/django_airavata/static/common/js/errors/GlobalErrorHandler.js
index 99dfefb74..ad6aef25f 100644
--- 
a/airavata-django-portal/django_airavata/static/common/js/errors/GlobalErrorHandler.js
+++ 
b/airavata-django-portal/django_airavata/static/common/js/errors/GlobalErrorHandler.js
@@ -1,11 +1,17 @@
-import Vue from "vue";
 import { errors } from "django-airavata-api";
 
 class GlobalErrorHandler {
   init() {
     console.log("Initializing GlobalErrorHandler..."); // eslint-disable-line 
no-console
     window.onerror = this.handleGlobalError;
-    Vue.config.errorHandler = this.vueGlobalErrorHandler;
+  }
+
+  /**
+   * Install the Vue error handler on a Vue 3 app instance.
+   * Call this after createApp() to attach the Vue error handler.
+   */
+  installVueErrorHandler(app) {
+    app.config.errorHandler = this.vueGlobalErrorHandler;
   }
 
   handleGlobalError(msg, url, lineNo, columnNo, error) {
diff --git a/airavata-django-portal/django_airavata/static/common/js/main.js 
b/airavata-django-portal/django_airavata/static/common/js/main.js
index 52575a24d..3608f83f5 100644
--- a/airavata-django-portal/django_airavata/static/common/js/main.js
+++ b/airavata-django-portal/django_airavata/static/common/js/main.js
@@ -1,17 +1,15 @@
-import bootstrap from "bootstrap"; // eslint-disable-line no-unused-vars
-import $ from "jquery";
-
-// Expose jQuery on the global object
-window.$ = window.jQuery = $;
+import * as bootstrap from "bootstrap";
 
 import "bootstrap/dist/css/bootstrap.css";
 import "@fortawesome/fontawesome-free/css/all.css";
 import "../scss/main.scss";
-$(function () {
-  $('[data-toggle="tooltip"]').tooltip();
-});
 
-// CMS integration
-// $('.carousel').carousel({
-//   interval: 2000
-// })
+// Initialize Bootstrap tooltips
+document.addEventListener("DOMContentLoaded", () => {
+  const tooltipTriggerList = document.querySelectorAll(
+    '[data-bs-toggle="tooltip"]'
+  );
+  tooltipTriggerList.forEach(
+    (tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
+  );
+});
diff --git 
a/airavata-django-portal/django_airavata/static/common/js/mixins/VModelMixin.js 
b/airavata-django-portal/django_airavata/static/common/js/mixins/VModelMixin.js
index d3887c089..b3c8068c6 100644
--- 
a/airavata-django-portal/django_airavata/static/common/js/mixins/VModelMixin.js
+++ 
b/airavata-django-portal/django_airavata/static/common/js/mixins/VModelMixin.js
@@ -4,21 +4,21 @@ export default {
   watch: {
     data: {
       handler: function (newValue, oldValue) {
-        // Only emit 'input' for objects when one of their deep properties has
+        // Only emit 'update:modelValue' for objects when one of their deep 
properties has
         // changed to prevent infinite loop since 'data' is recloned whenever
-        // 'value' changes
-        if (typeof this.value === "object" && newValue === oldValue) {
-          this.$emit("input", newValue);
+        // 'modelValue' changes
+        if (typeof this.modelValue === "object" && newValue === oldValue) {
+          this.$emit("update:modelValue", newValue);
         } else if (
-          (this.value === null || typeof this.value !== "object") &&
+          (this.modelValue === null || typeof this.modelValue !== "object") &&
           newValue !== oldValue
         ) {
-          this.$emit("input", newValue);
+          this.$emit("update:modelValue", newValue);
         }
       },
       deep: true,
     },
-    value: {
+    modelValue: {
       handler: function (newValue) {
         this.data = this.copyValue(newValue);
       },
@@ -45,12 +45,13 @@ export default {
   },
   data: function () {
     return {
-      data: this.copyValue(this.value),
+      data: this.copyValue(this.modelValue),
     };
   },
   props: {
-    value: {
+    modelValue: {
       required: true,
     },
   },
+  emits: ["update:modelValue"],
 };
diff --git a/airavata-django-portal/django_airavata/static/common/js/notices.js 
b/airavata-django-portal/django_airavata/static/common/js/notices.js
index 5ad32f63f..26fd32def 100644
--- a/airavata-django-portal/django_airavata/static/common/js/notices.js
+++ b/airavata-django-portal/django_airavata/static/common/js/notices.js
@@ -1,23 +1,25 @@
-import Vue from "vue";
+import { createApp, h } from "vue";
 import GatewayNoticesContainer from "./components/GatewayNoticesContainer";
 
-new Vue({
-  render(h) {
-    return h(GatewayNoticesContainer, {
-      props: {
+const el = document.getElementById("gateway-notices");
+if (el) {
+  const app = createApp({
+    data() {
+      return {
+        unreadCount: null,
+        notices: null,
+      };
+    },
+    beforeMount() {
+      this.unreadCount = parseInt(this.$el.dataset.unreadCount);
+      this.notices = JSON.parse(this.$el.dataset.notices);
+    },
+    render() {
+      return h(GatewayNoticesContainer, {
         unreadCount: this.unreadCount,
         notices: this.notices,
-      },
-    });
-  },
-  data() {
-    return {
-      unreadCount: null,
-      notices: null,
-    };
-  },
-  beforeMount() {
-    this.unreadCount = parseInt(this.$el.dataset.unreadCount);
-    this.notices = JSON.parse(this.$el.dataset.notices);
-  },
-}).$mount("#gateway-notices");
+      });
+    },
+  });
+  app.mount("#gateway-notices");
+}

Reply via email to