MT#64292 Migrate App.Vue and vue-wait to Vue 3

Modernize vue-wait initialization and usage patterns to support Vue 3
Composition API while preserving Vue 2 Options API functionality.

Changes:
- Migrate App.vue to Vue 3 <script setup> syntax
  - Convert Options API to Composition API
  - Use defineComponent and reactive state management
  - Maintain existing functionality and lifecycle hooks

- Refactor vue-wait initialization (boot/vue-wait.js)
  - Move initialization from App.vue mounted() to boot phase
  - Create initializeWait() function for explicit setup
  - Register $wait globally before component mounting

- Add Vue 3 composable support (composables/useWait.js)
  - Create useWait() composable for Composition API components
  - Access $wait via getCurrentInstance() for proper Vue 3 patterns
  - Enable gradual migration from Options to Composition API

- Update documentation (doc/architectural-overview.md)
  - Add "Loading State Management (vue-wait)" section
  - Document usage patterns for both Vue 2 and Vue 3
  - Clarify migration strategy and coexistence

Note:
All existing Vue 2 components using this.$wait continue working unchanged
while new Vue 3 components can use useWait() composable.

Change-Id: I62c9eaa81336a1ae115f1b326676497dcac262b2
mr14.1
Debora Crescenzo 5 months ago committed by Crescenzo Debora
parent d575fe2dd5
commit db5e5b55eb

@ -100,10 +100,38 @@ VoIP notes and constraints:
Starting with Vue 3 migration, the application introduces composables (`src/composables/`) as an optional layer for accessing Vuex store in Composition API components: Starting with Vue 3 migration, the application introduces composables (`src/composables/`) as an optional layer for accessing Vuex store in Composition API components:
- **Purpose**: Provide reactive, encapsulated access to store state and actions - **Purpose**: Provide reactive, encapsulated access to store state and actions
- **Available composables**: `useStore.js` (generic), `useUser.js` (authentication), `usePbx.js` (PBX features), `useGlobals.js` (global properties) - **Available composables**: `useStore.js` (generic), `useUser.js` (authentication), `usePbx.js` (PBX features), `useGlobals.js` (global properties), `useWait.js` (loading states)
- **Coexistence**: Options API components continue using `mapState`/`mapGetters`/`mapActions`; composables are used when converting to Composition API - **Coexistence**: Options API components continue using `mapState`/`mapGetters`/`mapActions`; composables are used when converting to Composition API
- **Details**: See `migration-guide.md` for usage patterns and `composables.md` for API reference - **Details**: See `migration-guide.md` for usage patterns and `composables.md` for API reference
## Loading State Management (vue-wait)
The application uses `vue-wait-vue3` to manage loading states across the application:
- **Initialization**: `vue-wait` is initialized during the Quasar boot phase in `src/boot/vue-wait.js`, making the `$wait` instance globally available before any components mount
- **Integration**: Integrated with Vuex store (module name: `wait`) to provide reactive loading state management
- **Vue 2 compatibility**: Existing components using Options API can access loading states via `this.$wait.start()`, `this.$wait.end()`, and `this.$wait.is()` without any changes
```js
export default {
mounted() {
this.$wait.start('loading')
}
}
```
- **Vue 3 support**: New Composition API components can use the `useWait()` composable from `src/composables/useWait.js` to access the same functionality
```js
import { useWait } from 'src/composables/useWait'
const wait = useWait()
wait.start('loading')
```
- **Usage pattern**: Call `this.$wait.start('action-name')` before async operations, `this.$wait.end('action-name')` after completion, and check state with `this.$wait.is('action-name')` or `$wait.is('action-name')` in templates
- **Migration note**: Both patterns work simultaneously, allowing gradual migration from Vue 2 to Vue 3 syntax without breaking existing functionality
## Store Infrastructure ## Store Infrastructure
The Vuex store includes standardized helpers for request lifecycle management: The Vuex store includes standardized helpers for request lifecycle management:

@ -1,49 +1,40 @@
<template> <template>
<router-view /> <router-view />
</template> </template>
<script>
import _ from 'lodash' <script setup>
import { useMeta } from 'quasar'
import { APP_NAME } from 'src/constants' import { APP_NAME } from 'src/constants'
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
export default { const route = useRoute()
name: 'App', const pageTitle = ref('')
meta () {
return { const updateTitle = (route) => {
title: this.pageTitle,
titleTemplate: (title) => `${APP_NAME} - ${title}`
}
},
data () {
return {
pageTitle: ''
}
},
watch: {
$route (route) {
this.updateTitle(route)
}
},
async mounted () {
this.$initWait()
this.updateTitle(this.$route)
},
methods: {
updateTitle: function (route) {
if (route) { if (route) {
const titleElements = [] const titleElements = []
const title = _.get(route, 'meta.title', '') const title = route.meta?.title || ''
const subTitle = _.get(route, 'meta.subtitle', '') const subTitle = route.meta?.subtitle || ''
if (title !== '') { if (title !== '') {
titleElements.push(title) titleElements.push(title)
} }
if (subTitle !== '') { if (subTitle !== '') {
titleElements.push(subTitle) titleElements.push(subTitle)
} }
this.pageTitle = titleElements.join(' - ') || route.name || '' pageTitle.value = titleElements.join(' - ') || route.name || ''
} else { } else {
this.pageTitle = '' pageTitle.value = ''
}
}
} }
} }
watch(route, (newRoute) => {
updateTitle(newRoute)
}, { immediate: true })
useMeta(() => ({
title: pageTitle.value,
titleTemplate: (title) => `${APP_NAME} - ${title}`
}))
</script> </script>

@ -1,12 +1,17 @@
import { createVueWait } from 'vue-wait-vue3' import { createVueWait } from 'vue-wait-vue3'
export default ({ app }) => { export function initializeWait (app) {
app.config.globalProperties.$initWait = () => {
const VueWait = createVueWait({ const VueWait = createVueWait({
useVuex: true, useVuex: true,
vuexModuleName: 'wait', vuexModuleName: 'wait',
registerDirective: true registerDirective: true
}) })
app.use(VueWait) app.use(VueWait)
return VueWait
} }
export default ({ app }) => {
// Initialize wait immediately during boot
initializeWait(app)
} }

@ -0,0 +1,15 @@
import { getCurrentInstance } from 'vue'
export function useWait () {
const instance = getCurrentInstance()
if (!instance) {
throw new Error('useWait must be called within a component setup function')
}
const wait = instance.appContext.config.globalProperties.$wait
if (!wait) {
throw new Error('vue-wait is not initialized. Make sure vue-wait boot file runs before using useWait()')
}
return wait
}
Loading…
Cancel
Save