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 |