PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / v3 / src / assets / js / admin / admin.js
ameliabooking / v3 / src / assets / js / admin Last commit date
admin.js 22 hours ago appointment.js 22 hours ago booking.js 22 hours ago customers.js 22 hours ago event.js 22 hours ago package.js 22 hours ago payment.js 22 hours ago socialAuthOptions.js 22 hours ago status.js 22 hours ago useReactiveCustomize.js 22 hours ago
admin.js
272 lines
1 import { createApp, defineAsyncComponent } from 'vue/dist/vue.esm-bundler'
2 import { provide, ref, reactive, readonly, nextTick, onMounted } from 'vue'
3 import { MazUi } from 'maz-ui/plugins'
4 import { mazUi as mazUiThemePreset } from '@maz-ui/themes/presets/mazUi'
5 import 'maz-ui/styles'
6 import { useLicence } from '../common/licence'
7 import store from '../../../store'
8
9 window.stylesInjectedV3 = async function () {}
10
11 const CustomizeCatalogWrapper = defineAsyncComponent({
12 loader: () => import('../../../views/admin/customize/pages/CustomizeCatalog.vue'),
13 })
14
15 const CustomizeCustomerPanelWrapper = defineAsyncComponent({
16 loader: () => import('../../../views/admin/customize/pages/CustomizeCustomerPanel.vue'),
17 })
18
19 const CustomizeEmployeePanelWrapper = defineAsyncComponent({
20 loader: () => import('../../../views/admin/customize/pages/CustomizeEmployeePanel.vue'),
21 })
22
23 const CustomizeEventCalendarWrapper = defineAsyncComponent({
24 loader: () => import('../../../views/admin/customize/pages/CustomizeEventCalendar.vue'),
25 })
26
27 const CustomizeEventListWrapper = defineAsyncComponent({
28 loader: () => import('../../../views/admin/customize/pages/CustomizeEventList.vue'),
29 })
30
31 const CustomizeStepNewWrapper = defineAsyncComponent({
32 loader: () => import('../../../views/admin/customize/pages/CustomizeStepNew.vue'),
33 })
34
35 const dynamicCdn = window.wpAmeliaUrls.wpAmeliaPluginURL + 'v3/public/'
36
37 window.__dynamic_handler__ = function (importer) {
38 return dynamicCdn + 'assets/' + importer
39 }
40 // @ts-ignore
41 window.__dynamic_preload__ = function (preloads) {
42 return preloads.map((preload) => dynamicCdn + preload)
43 }
44
45 function getMazLocale() {
46 const locale = Array.isArray(window.localeLanguage) ? window.localeLanguage[0] : ''
47
48 if (typeof locale !== 'string' || !locale) {
49 return 'en'
50 }
51
52 // Normalize WP locale formats like en_US -> en for maz-ui locale keys.
53 return locale.toLowerCase().replace('_', '-').split('-')[0] || 'en'
54 }
55
56 function getVueStyleElements() {
57 let styleElements = []
58 if (import.meta.env.DEV) {
59 if (window.customizeComponentLoaded) {
60 styleElements = document.querySelectorAll('#vite-dev-vue3')
61 }
62 } else {
63 // Collect stylesheet link elements injected by Vue in production mode
64 const linkElements = Array.from(document.getElementsByTagName('link')).filter(
65 (el) =>
66 el.getAttribute('rel') === 'stylesheet' &&
67 el.getAttribute('href')?.includes('ameliabooking/v3/public/assets'),
68 )
69
70 // Collect inline style elements that contain /* purgecss start ignore */
71 // These are styles injected by maz-ui and other third-party components
72 const inlineStyleElements = Array.from(document.getElementsByTagName('style')).filter((el) =>
73 el.textContent?.includes('/* purgecss start ignore */'),
74 )
75
76 styleElements = [...linkElements, ...inlineStyleElements]
77 }
78 return styleElements
79 }
80
81 async function injectVueStyles(shadowRoot) {
82 if (!shadowRoot) return
83
84 const waitForStyles = () =>
85 new Promise((resolve) => {
86 // Check immediately first
87 const checkStyles = () => {
88 const styles = getVueStyleElements()
89 if (styles !== undefined && styles.length > 0 && window.customizeComponentLoaded) {
90 return styles
91 }
92 return null
93 }
94
95 const immediateResult = checkStyles()
96 if (immediateResult) {
97 resolve(immediateResult)
98 return
99 }
100
101 // Use MutationObserver to watch for style changes in <head>
102 const observer = new MutationObserver(() => {
103 const styles = checkStyles()
104 if (styles) {
105 observer.disconnect()
106 resolve(styles)
107 }
108 })
109
110 observer.observe(document.head, {
111 childList: true,
112 subtree: true,
113 })
114
115 // Fallback for customizeComponentLoaded flag using requestAnimationFrame
116 const fallbackCheck = () => {
117 const styles = checkStyles()
118 if (styles) {
119 observer.disconnect()
120 resolve(styles)
121 } else if (!window.customizeComponentLoaded) {
122 requestAnimationFrame(fallbackCheck)
123 }
124 }
125 requestAnimationFrame(fallbackCheck)
126 })
127
128 const styleElements = await waitForStyles()
129 const fragment = document.createDocumentFragment()
130
131 styleElements.forEach((node, index) => {
132 const clone = node.cloneNode(true)
133 clone.id = `v3-style-${index}`
134
135 // For link elements, ensure href is preserved correctly
136 if (node.tagName === 'LINK' && node.href) {
137 clone.href = node.href
138 }
139
140 fragment.appendChild(clone)
141 })
142
143 shadowRoot.appendChild(fragment)
144 window.stylesInjectedV3()
145 }
146
147 // Function to wait for mount element and then mount the app
148 function waitForMountElement() {
149 const host = document.getElementsByClassName('amd-cp__preview')[0]
150 const shadowRoot = host?.shadowRoot
151 const mountElement = shadowRoot?.getElementById('amelia-app-backend-new')
152
153 if (!mountElement) {
154 setTimeout(waitForMountElement, 100)
155 return
156 }
157
158 const componentToMount =
159 window.v3?.componentToMount !== undefined ? window.v3.componentToMount : 'Customize'
160
161 let selectedComponent
162 let componentName
163
164 switch (componentToMount) {
165 case 'CustomizeCatalog':
166 selectedComponent = CustomizeCatalogWrapper
167 componentName = 'CustomizeCatalog'
168 break
169 case 'CustomizeCustomerPanel':
170 selectedComponent = CustomizeCustomerPanelWrapper
171 componentName = 'CustomizeCustomerPanel'
172 break
173 case 'CustomizeEmployeePanel':
174 selectedComponent = CustomizeEmployeePanelWrapper
175 componentName = 'CustomizeEmployeePanel'
176 break
177 case 'CustomizeEventCalendar':
178 selectedComponent = CustomizeEventCalendarWrapper
179 componentName = 'CustomizeEventCalendar'
180 break
181 case 'CustomizeEventList':
182 selectedComponent = CustomizeEventListWrapper
183 componentName = 'CustomizeEventList'
184 break
185 case 'CustomizeStepNew':
186 selectedComponent = CustomizeStepNewWrapper
187 componentName = 'CustomizeStepNew'
188 break
189 default:
190 selectedComponent = CustomizeStepNewWrapper
191 componentName = 'CustomizeStepNew'
192 }
193
194 createApp({
195 setup() {
196 const baseURLs = ref(window.wpAmeliaUrls)
197 const languages = reactive(window.wpAmeliaLanguages)
198 const settings = reactive(window.wpAmeliaSettings)
199 const timeZone = ref('wpAmeliaTimeZone' in window ? window.wpAmeliaTimeZone[0] : '')
200 const localLanguage = ref(window.localeLanguage[0])
201 const labels = reactive(window.wpAmeliaLabels)
202 const licence = reactive(useLicence())
203
204 provide('settings', readonly(settings))
205 provide('baseUrls', readonly(baseURLs))
206 provide('timeZone', readonly(timeZone))
207 provide('localLanguage', readonly(localLanguage))
208 provide('languages', readonly(languages))
209 provide('labels', readonly(labels))
210 provide('licence', licence)
211
212 onMounted(async () => {
213 await injectVueStyles(shadowRoot)
214 })
215 },
216 template: `<${componentName} />`,
217 })
218 .component(componentName, selectedComponent)
219 .use(MazUi, {
220 theme: {
221 preset: mazUiThemePreset,
222 strategy: 'runtime',
223 darkModeStrategy: 'class',
224 },
225 translations: {
226 locale: getMazLocale(),
227 fallbackLocale: 'en',
228 },
229 })
230 .use(store)
231 .mount(mountElement)
232 }
233
234 // Add this before calling mountUnmountV3App
235 if (!window.onUnmountedV3Callback) {
236 window.onUnmountedV3Callback = async () => {}
237 }
238
239 async function unmountV3App() {
240 const host = document.getElementsByClassName('amd-cp__preview')[0]
241 const shadowRoot = host?.shadowRoot
242 const mountElement = shadowRoot?.getElementById('amelia-app-backend-new')
243 const appInstance = mountElement?.__vue_app__
244
245 // remove injected styles from shadow DOM
246 const styleElements = shadowRoot?.querySelectorAll('[id^="v3-style-"]')
247 styleElements?.forEach((styleElement) => {
248 styleElement.remove()
249 })
250
251 if (appInstance) {
252 appInstance.unmount()
253
254 await nextTick()
255
256 // Verify unmount by checking if the mount element is empty
257 const isUnmounted = !mountElement?.innerHTML || mountElement.innerHTML.trim() === ''
258
259 if (typeof window.onUnmountedV3Callback === 'function' && isUnmounted) {
260 await window.onUnmountedV3Callback()
261 }
262 }
263 }
264
265 window.mountUnmountV3App = async () => {
266 if (window.v3) {
267 waitForMountElement()
268 } else {
269 await unmountV3App()
270 }
271 }
272