@vc-shell/framework 1.0.293 → 1.0.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/core/composables/useLanguages/index.ts +26 -0
  3. package/core/composables/useMenuService/index.ts +1 -1
  4. package/core/composables/useSettings/index.ts +2 -3
  5. package/core/composables/useTheme/index.ts +2 -1
  6. package/core/composables/useUser/index.ts +4 -76
  7. package/core/constants/index.ts +1 -0
  8. package/core/constants/locale.ts +78 -0
  9. package/core/interceptors/index.ts +3 -0
  10. package/core/plugins/modularity/index.ts +106 -16
  11. package/dist/core/composables/useLanguages/index.d.ts +5 -0
  12. package/dist/core/composables/useLanguages/index.d.ts.map +1 -1
  13. package/dist/core/composables/useSettings/index.d.ts.map +1 -1
  14. package/dist/core/composables/useTheme/index.d.ts.map +1 -1
  15. package/dist/core/composables/useUser/index.d.ts +1 -3
  16. package/dist/core/composables/useUser/index.d.ts.map +1 -1
  17. package/dist/core/constants/index.d.ts +2 -0
  18. package/dist/core/constants/index.d.ts.map +1 -0
  19. package/dist/core/constants/locale.d.ts +7 -0
  20. package/dist/core/constants/locale.d.ts.map +1 -0
  21. package/dist/core/interceptors/index.d.ts.map +1 -1
  22. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  23. package/dist/framework.js +34165 -30178
  24. package/dist/index.css +1 -1
  25. package/dist/index.d.ts +9 -4
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/locales/en.json +2 -1
  28. package/dist/shared/components/app-switcher/components/vc-app-switcher/vc-app-switcher.vue.d.ts.map +1 -1
  29. package/dist/shared/components/index.d.ts +1 -0
  30. package/dist/shared/components/index.d.ts.map +1 -1
  31. package/dist/shared/components/language-selector/language-selector.vue.d.ts.map +1 -1
  32. package/dist/shared/components/sidebar/sidebar.vue.d.ts +1 -1
  33. package/dist/shared/components/sidebar/sidebar.vue.d.ts.map +1 -1
  34. package/dist/shared/components/sign-in/azuread.vue.d.ts +17 -0
  35. package/dist/shared/components/sign-in/azuread.vue.d.ts.map +1 -0
  36. package/dist/shared/components/sign-in/external-provider.vue.d.ts +23 -0
  37. package/dist/shared/components/sign-in/external-provider.vue.d.ts.map +1 -0
  38. package/dist/shared/components/sign-in/external-providers.vue.d.ts +16 -0
  39. package/dist/shared/components/sign-in/external-providers.vue.d.ts.map +1 -0
  40. package/dist/shared/components/sign-in/index.d.ts +2 -0
  41. package/dist/shared/components/sign-in/index.d.ts.map +1 -0
  42. package/dist/shared/components/sign-in/useExternalProvider.d.ts +12 -0
  43. package/dist/shared/components/sign-in/useExternalProvider.d.ts.map +1 -0
  44. package/dist/shared/components/theme-selector/theme-selector.vue.d.ts.map +1 -1
  45. package/dist/shared/components/user-dropdown-button/user-dropdown-button.vue.d.ts.map +1 -1
  46. package/dist/shared/modules/dynamic/index.d.ts +13 -10
  47. package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
  48. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts +1 -0
  49. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +1 -1
  50. package/dist/shared/pages/LoginPage/components/login/Login.vue.d.ts.map +1 -1
  51. package/dist/tailwind.config.d.ts +1 -1
  52. package/dist/tailwind.config.d.ts.map +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/dist/ui/components/atoms/vc-image/index.d.ts +1 -68
  55. package/dist/ui/components/atoms/vc-image/index.d.ts.map +1 -1
  56. package/dist/ui/components/atoms/vc-image/vc-image.stories.d.ts +3 -3
  57. package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts +2 -2
  58. package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts.map +1 -1
  59. package/dist/ui/components/atoms/vc-tooltip/vc-tooltip.vue.d.ts.map +1 -1
  60. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue.d.ts +2 -2
  61. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue.d.ts.map +1 -1
  62. package/dist/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue.d.ts.map +1 -1
  63. package/dist/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue.d.ts +1 -1
  64. package/dist/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue.d.ts.map +1 -1
  65. package/dist/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue.d.ts.map +1 -1
  66. package/dist/ui/components/organisms/vc-table/vc-table.stories.d.ts +30 -30
  67. package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts +9 -6
  68. package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
  69. package/package.json +6 -6
  70. package/shared/components/app-switcher/components/vc-app-switcher/vc-app-switcher.vue +1 -2
  71. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +1 -1
  72. package/shared/components/index.ts +1 -0
  73. package/shared/components/language-selector/language-selector.vue +59 -6
  74. package/shared/components/sidebar/sidebar.vue +1 -1
  75. package/shared/components/sign-in/azuread.vue +24 -0
  76. package/shared/components/sign-in/external-provider.vue +38 -0
  77. package/shared/components/sign-in/external-providers.vue +39 -0
  78. package/shared/components/sign-in/index.ts +1 -0
  79. package/shared/components/sign-in/useExternalProvider.ts +102 -0
  80. package/shared/components/theme-selector/theme-selector.vue +7 -8
  81. package/shared/components/user-dropdown-button/user-dropdown-button.vue +5 -0
  82. package/shared/modules/dynamic/helpers/nodeBuilder.ts +2 -2
  83. package/shared/modules/dynamic/helpers/override.ts +1 -1
  84. package/shared/modules/dynamic/index.ts +166 -63
  85. package/shared/modules/dynamic/pages/dynamic-blade-form.vue +99 -20
  86. package/shared/modules/dynamic/pages/dynamic-blade-list.vue +1 -1
  87. package/shared/pages/LoginPage/components/login/Login.vue +8 -40
  88. package/tailwind.config.ts +6 -198
  89. package/ui/components/atoms/vc-image/index.ts +1 -3
  90. package/ui/components/atoms/vc-image/vc-image.vue +3 -2
  91. package/ui/components/atoms/vc-tooltip/vc-tooltip.vue +7 -2
  92. package/ui/components/molecules/vc-field/_internal/vc-field-type/vc-field-type.vue +1 -1
  93. package/ui/components/molecules/vc-input/vc-input.vue +38 -0
  94. package/ui/components/molecules/vc-input-currency/vc-input-currency.vue +1 -1
  95. package/ui/components/molecules/vc-select/vc-select.vue +15 -5
  96. package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +1 -1
  97. package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +2 -24
  98. package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +12 -3
  99. package/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue +1 -1
  100. package/ui/components/organisms/vc-table/_internal/vc-table-mobile-item/vc-table-mobile-item.vue +2 -0
  101. package/ui/components/organisms/vc-table/vc-table.vue +124 -65
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [1.0.295](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.294...v1.0.295) (2024-10-01)
2
+
3
+
4
+
5
+ ## [1.0.294](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.293...v1.0.294) (2024-10-01)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **ui:** components fixes and updates ([37148ec](https://github.com/VirtoCommerce/vc-shell/commit/37148ec889065d7e76c7c3466f5bc23a86080389))
11
+ * **ui:** sidebar component optional position prop ([61a7018](https://github.com/VirtoCommerce/vc-shell/commit/61a7018f88d42f0a84c6d3fbe9b3318f94152c99))
12
+
13
+
14
+ ### Features
15
+
16
+ * **dynamic:** mixin support, refactor of module registering ([70589c9](https://github.com/VirtoCommerce/vc-shell/commit/70589c9a9044af31ffbbb66189bd96456a3e5d0c))
17
+ * locale flag ([0dc7f74](https://github.com/VirtoCommerce/vc-shell/commit/0dc7f740ed24acee2dfd7efbb0880d9b4e7ab4c7))
18
+ * **ui:** vc-table resizing, state saving improvements ([cd402b1](https://github.com/VirtoCommerce/vc-shell/commit/cd402b14e1442d8072b7572959eb2b6c97218fac))
19
+
20
+
21
+
1
22
  ## [1.0.293](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.290...v1.0.293) (2024-09-23)
2
23
 
3
24
 
@@ -4,11 +4,14 @@ import { setLocale as setVeeI18nLocale, localize } from "@vee-validate/i18n";
4
4
  import { createSharedComposable, useLocalStorage } from "@vueuse/core";
5
5
  import { ComputedRef, computed } from "vue";
6
6
  import ISO6391 from "iso-639-1";
7
+ import { languageToCountryMap } from "../../constants";
7
8
 
8
9
  export interface IUseLanguages {
9
10
  setLocale: (locale: string) => void;
10
11
  currentLocale: ComputedRef<string>;
11
12
  getLocaleByTag: (localeTag: string) => string | undefined;
13
+ resolveCamelCaseLocale: (locale: string) => string;
14
+ getFlag: (language: string) => Promise<string>;
12
15
  }
13
16
 
14
17
  export const useLanguages = createSharedComposable(() => {
@@ -44,9 +47,32 @@ export const useLanguages = createSharedComposable(() => {
44
47
  return ISO6391.getNativeName(twoLetterLanguageName);
45
48
  }
46
49
 
50
+ function resolveCamelCaseLocale(locale: string) {
51
+ const formattedLocale = locale.replace(/([a-z]+)([A-Z]+)/g, "$1-$2").toLowerCase();
52
+
53
+ if (i18n.global.getLocaleMessage(formattedLocale)) {
54
+ return formattedLocale;
55
+ }
56
+ return "en";
57
+ }
58
+
59
+ function getCountryCode(language: string): string {
60
+ return (
61
+ languageToCountryMap[language.toLocaleLowerCase()] || languageToCountryMap[language.slice(0, 2)] || "xx" // placeholder for unknown country
62
+ );
63
+ }
64
+
65
+ async function getFlag(language: string): Promise<string> {
66
+ const countryCode = getCountryCode(language);
67
+ return new URL(`../../../assets/icons/flags/${countryCode}.svg`, import.meta.url).href;
68
+ }
69
+
47
70
  return {
48
71
  setLocale,
49
72
  currentLocale,
50
73
  getLocaleByTag,
74
+ resolveCamelCaseLocale,
75
+ getFlag,
76
+ getCountryCode,
51
77
  };
52
78
  });
@@ -23,7 +23,7 @@ function useMenuServiceFn(): MenuService {
23
23
 
24
24
  const upsert = createUnrefFn(
25
25
  (array: (MenuItem | Omit<MenuItem, "icon">)[], element: MenuItem | Omit<MenuItem, "icon">) => {
26
- const index = array.findIndex((_element) => _.isEqual(_element, element));
26
+ const index = array.findIndex((_element) => _.isEqual(_.omit(_element, "title"), _.omit(element, "title")));
27
27
  if (index > -1) {
28
28
  array[index] = { ...element };
29
29
  } else {
@@ -19,7 +19,6 @@ interface IUseSettings {
19
19
  }
20
20
 
21
21
  export function useSettings(): IUseSettings {
22
- const base = inject("platformUrl");
23
22
  const uiSettings = ref<IUISetting | undefined>();
24
23
 
25
24
  const { getApiClient } = useApiClient(SettingClient);
@@ -30,8 +29,8 @@ export function useSettings(): IUseSettings {
30
29
 
31
30
  if (settings) {
32
31
  uiSettings.value = {
33
- contrast_logo: base + settings.contrast_logo,
34
- logo: base + settings.logo,
32
+ contrast_logo: settings.contrast_logo,
33
+ logo: settings.logo,
35
34
  title: settings.title,
36
35
  };
37
36
  }
@@ -10,7 +10,7 @@ export interface IUseTheme {
10
10
  setTheme: (theme: string) => void;
11
11
  }
12
12
 
13
- const registeredThemes: Ref<string[]> = ref([]);
13
+ const registeredThemes: Ref<string[]> = ref(["auto"]);
14
14
  export const useTheme = (): IUseTheme => {
15
15
  function register(customNames: string | string[]) {
16
16
  (typeof customNames === "string" ? [customNames] : customNames).forEach((name) => {
@@ -34,6 +34,7 @@ export const useTheme = (): IUseTheme => {
34
34
  }
35
35
 
36
36
  const mode = useColorMode({
37
+ emitAuto: true,
37
38
  modes: {
38
39
  ...registeredThemes.value.reduce(
39
40
  (acc, name) => {
@@ -7,17 +7,14 @@ import {
7
7
  SecurityResult,
8
8
  ValidatePasswordResetTokenRequest,
9
9
  IdentityResult,
10
- ExternalSignInClient,
11
10
  ChangePasswordRequest,
12
11
  LoginType,
13
- ExternalSignInProviderInfo,
14
12
  LoginRequest,
15
13
  SignInResult,
16
14
  } from "./../../api/platform";
17
15
  import { RequestPasswordResult } from "./../../types";
18
- import { createSharedComposable, useLocalStorage } from "@vueuse/core";
19
-
20
- const VC_EXTERNAL_AUTH_DATA_KEY = "externalSignIn";
16
+ import { createSharedComposable } from "@vueuse/core";
17
+ import { useExternalProvider } from "../../../shared/components/sign-in/useExternalProvider";
21
18
 
22
19
  interface IUseUser {
23
20
  user: ComputedRef<UserDetail | undefined>;
@@ -32,8 +29,6 @@ interface IUseUser {
32
29
  resetPasswordByToken: (userId: string, password: string, token: string) => Promise<SecurityResult>;
33
30
  requestPasswordReset: (loginOrEmail: string) => Promise<RequestPasswordResult>;
34
31
  changeUserPassword: (oldPassword: string, newPassword: string) => Promise<SecurityResult | undefined>;
35
- getExternalLoginProviders: () => Promise<ExternalSignInProviderInfo[] | undefined>;
36
- externalSignIn: (authenticationType?: string | undefined, returnUrl?: string | undefined) => void;
37
32
  getLoginType: () => Promise<LoginType[]>;
38
33
  isAuthenticated: ComputedRef<boolean>;
39
34
  }
@@ -41,17 +36,9 @@ const user: Ref<UserDetail | undefined> = ref();
41
36
  function useUserFn(): IUseUser {
42
37
  const loading: Ref<boolean> = ref(false);
43
38
 
39
+ const { storage: externalSignInStorage, signOut: externalSignOut } = useExternalProvider();
40
+
44
41
  const securityClient = new SecurityClient();
45
- const base = window.location.origin + "/";
46
- const externalSecurityClient = new ExternalSignInClient(base);
47
- const externalSignInStorage = useLocalStorage<{ providerType?: string | undefined }>(
48
- VC_EXTERNAL_AUTH_DATA_KEY,
49
- {},
50
- {
51
- listenToStorageChanges: true,
52
- deep: true,
53
- },
54
- );
55
42
 
56
43
  const isAuthenticated = computed(() => user.value?.userName != null);
57
44
 
@@ -183,63 +170,6 @@ function useUserFn(): IUseUser {
183
170
  return result;
184
171
  }
185
172
 
186
- async function getExternalLoginProviders() {
187
- let result: ExternalSignInProviderInfo[] | undefined = undefined;
188
- try {
189
- result = await externalSecurityClient.getExternalLoginProviders();
190
- } catch (e) {
191
- console.error(e);
192
- // TODO check error in app!!!
193
- }
194
-
195
- return result;
196
- }
197
-
198
- async function externalSignIn(authenticationType: string | undefined, returnUrl: string | undefined) {
199
- try {
200
- let url_ = base + "externalsignin?";
201
-
202
- const signInData = {
203
- providerType: authenticationType,
204
- };
205
-
206
- externalSignInStorage.value = signInData;
207
-
208
- if (authenticationType === null) throw new Error("The parameter 'authenticationType' cannot be null.");
209
- else {
210
- if (authenticationType !== undefined)
211
- url_ += "authenticationType=" + encodeURIComponent("" + authenticationType) + "&";
212
- if (returnUrl !== undefined) url_ += "returnUrl=" + encodeURIComponent("" + returnUrl) + "&";
213
- }
214
- url_ = url_.replace(/[?&]$/, "");
215
-
216
- window.location.href = url_;
217
- } catch (e) {
218
- console.error(e);
219
-
220
- throw e;
221
- }
222
- }
223
-
224
- async function externalSignOut(authenticationType: string): Promise<void> {
225
- try {
226
- let url_ = base + "externalsignin/signout?";
227
-
228
- if (authenticationType !== undefined)
229
- url_ += "authenticationType=" + encodeURIComponent("" + authenticationType) + "&";
230
- if (window.location.pathname !== undefined)
231
- url_ += "returnUrl=" + encodeURIComponent("" + window.location.pathname) + "&";
232
-
233
- url_ = url_.replace(/[?&]$/, "");
234
- window.location.href = url_;
235
-
236
- externalSignInStorage.value = {};
237
- } catch (e) {
238
- console.error(e);
239
- throw e;
240
- }
241
- }
242
-
243
173
  return {
244
174
  user: computed(() => user.value),
245
175
  loading: computed(() => loading.value),
@@ -253,8 +183,6 @@ function useUserFn(): IUseUser {
253
183
  resetPasswordByToken,
254
184
  requestPasswordReset,
255
185
  changeUserPassword,
256
- getExternalLoginProviders,
257
- externalSignIn,
258
186
  getLoginType,
259
187
  };
260
188
  }
@@ -0,0 +1 @@
1
+ export * from "./locale";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Mapping ISO 639-1 language codes (and culture codes) to ISO 3166-1 alpha-2 country codes
3
+ * @see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
4
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
5
+ */
6
+ export const languageToCountryMap: Record<string, string> = {
7
+ af: "za",
8
+ ar: "sa",
9
+ az: "az",
10
+ be: "by",
11
+ bg: "bg",
12
+ bn: "bd",
13
+ bs: "ba",
14
+ ca: "es",
15
+ cs: "cz",
16
+ cy: "gb",
17
+ da: "dk",
18
+ de: "de",
19
+ el: "gr",
20
+ en: "us",
21
+ "en-gb": "gb",
22
+ es: "es",
23
+ et: "ee",
24
+ fa: "ir",
25
+ fi: "fi",
26
+ fil: "ph",
27
+ fr: "fr",
28
+ gu: "in",
29
+ he: "il",
30
+ hi: "in",
31
+ hr: "hr",
32
+ hu: "hu",
33
+ hy: "am",
34
+ id: "id",
35
+ is: "is",
36
+ it: "it",
37
+ ja: "jp",
38
+ ka: "ge",
39
+ kk: "kz",
40
+ km: "kh",
41
+ kn: "in",
42
+ ko: "kr",
43
+ ky: "kg",
44
+ lt: "lt",
45
+ lv: "lv",
46
+ mk: "mk",
47
+ ml: "in",
48
+ mn: "mn",
49
+ mr: "in",
50
+ ms: "my",
51
+ mt: "mt",
52
+ ne: "np",
53
+ nl: "nl",
54
+ no: "no",
55
+ pa: "in",
56
+ pl: "pl",
57
+ pt: "pt",
58
+ ro: "ro",
59
+ ru: "ru",
60
+ si: "lk",
61
+ sk: "sk",
62
+ sl: "si",
63
+ sq: "al",
64
+ sr: "rs",
65
+ sv: "se",
66
+ sw: "tz",
67
+ ta: "in",
68
+ te: "in",
69
+ th: "th",
70
+ tr: "tr",
71
+ uk: "ua",
72
+ ur: "pk",
73
+ uz: "uz",
74
+ vi: "vn",
75
+ yo: "ng",
76
+ zh: "cn",
77
+ zu: "za",
78
+ };
@@ -56,6 +56,9 @@ export function registerInterceptors(router: Router) {
56
56
  if (isAuthenticated.value) {
57
57
  signOut().then(() => {
58
58
  redirect(router);
59
+ notification.error(
60
+ "Access Denied: Your session has expired or you do not have the necessary permissions.\nPlease log in again or contact the administrator for assistance.",
61
+ );
59
62
  });
60
63
  }
61
64
  }
@@ -10,12 +10,21 @@ export const createModule = (components: { [key: string]: BladeInstanceConstruct
10
10
  install(app: App): void {
11
11
  // Register components
12
12
  Object.entries(components).forEach(([componentName, component]) => {
13
+ // Check if the component is already registered
14
+ if (app.component(componentName)) {
15
+ // Remove the existing component
16
+ // Note: Vue does not provide a method to remove a component, so we can overwrite it
17
+ console.warn(
18
+ `Component ${componentName} is already registered. It will be overwritten with the new component.`,
19
+ );
20
+ }
13
21
  app.component(componentName, component);
14
22
  });
15
23
 
16
24
  // Load locales
17
25
  if (locales) {
18
26
  Object.entries(locales).forEach(([key, message]) => {
27
+ // Merge locale messages, overwriting existing ones
19
28
  i18n.global.mergeLocaleMessage(key, message);
20
29
  });
21
30
  }
@@ -45,33 +54,66 @@ export const createAppModule = (
45
54
  page.routable = true;
46
55
  }
47
56
 
48
- // all components registered through this plugin are blades
57
+ // All components registered through this plugin are blades
49
58
  page.isBlade = true;
50
59
 
51
60
  if (!page.url) {
61
+ // Remove existing page if it exists
62
+ if (app.config.globalProperties.pages) {
63
+ const existingPageIndex = app.config.globalProperties.pages.findIndex(
64
+ (p: BladeInstanceConstructor) => p.name === page.name,
65
+ );
66
+ if (existingPageIndex !== -1) {
67
+ app.config.globalProperties.pages.splice(existingPageIndex, 1);
68
+ }
69
+ }
70
+
71
+ // Add new page
52
72
  app.config.globalProperties.pages?.push(page);
53
73
 
74
+ // Remove existing bladeRoute if it exists
75
+ if (app.config.globalProperties.bladeRoutes) {
76
+ const existingBladeRouteIndex = app.config.globalProperties.bladeRoutes.findIndex(
77
+ (r: BladeInstanceConstructor) => r.name === page.name,
78
+ );
79
+ if (existingBladeRouteIndex !== -1) {
80
+ app.config.globalProperties.bladeRoutes.splice(existingBladeRouteIndex, 1);
81
+ }
82
+ }
83
+
84
+ // Add new bladeRoute
54
85
  app.config.globalProperties.bladeRoutes?.push({
55
86
  component: page,
56
87
  name: page?.name,
57
88
  isWorkspace: page.isWorkspace || false,
58
89
  });
59
90
 
60
- if (page.name) app.component(page.name, page);
91
+ // Register component globally
92
+ if (page.name) {
93
+ if (app.component(page.name)) {
94
+ // Overwrite existing component
95
+ console.warn(
96
+ `Component ${page.name} is already registered. It will be overwritten with the new component.`,
97
+ );
98
+ }
99
+ app.component(page.name, page);
100
+ }
61
101
  }
62
102
 
63
103
  if (!page.moduleUid) {
64
104
  page.moduleUid = uid;
65
105
  }
66
106
 
67
- // Dynamically add pages to vue router
107
+ // Dynamically add pages to Vue Router
68
108
  if (page.url) {
69
- const mainRouteName = routerInstance.getRoutes().find((r) => r.meta?.root)?.name;
109
+ const mainRoute = routerInstance.getRoutes().find((r) => r.meta?.root);
70
110
 
71
- if (!mainRouteName) {
72
- throw new Error("No parent route is found. Make sure you have added `meta: {root: true}` to main route.");
111
+ if (!mainRoute) {
112
+ throw new Error("Main route not found. Make sure you have added `meta: {root: true}` to the main route.");
73
113
  }
74
114
 
115
+ const mainRouteName = mainRoute.name;
116
+
75
117
  const routeName = page.name || kebabToPascal(page.url.substring(1));
76
118
 
77
119
  const BladeInstanceConstructor = Object.assign({}, page, { name: routeName });
@@ -80,19 +122,45 @@ export const createAppModule = (
80
122
  navigation: {},
81
123
  }) as BladeVNode;
82
124
 
83
- if (routerInstance && mainRouteName) {
84
- routerInstance.addRoute(mainRouteName, {
85
- name: routeName,
86
- path: page.url.substring(1),
87
- components: { default: bladeVNode },
88
- meta: {
89
- permissions: page?.permissions,
90
- },
91
- });
125
+ // Remove existing route if it exists
126
+ if (routerInstance.hasRoute(routeName)) {
127
+ routerInstance.removeRoute(routeName);
128
+ }
129
+
130
+ // Add new route
131
+ routerInstance.addRoute(mainRouteName as string, {
132
+ name: routeName,
133
+ path: page.url.substring(1),
134
+ components: { default: bladeVNode },
135
+ meta: {
136
+ permissions: page?.permissions,
137
+ },
138
+ });
139
+
140
+ // Remove existing page in global properties if it exists
141
+ if (app.config.globalProperties.pages) {
142
+ const existingPageIndex = app.config.globalProperties.pages.findIndex(
143
+ (p: BladeInstanceConstructor) => p.name === bladeVNode.type.name,
144
+ );
145
+ if (existingPageIndex !== -1) {
146
+ app.config.globalProperties.pages.splice(existingPageIndex, 1);
147
+ }
92
148
  }
93
149
 
150
+ // Add new page to global properties
94
151
  app.config.globalProperties.pages?.push(bladeVNode);
95
152
 
153
+ // Remove existing bladeRoute if it exists
154
+ if (app.config.globalProperties.bladeRoutes) {
155
+ const existingBladeRouteIndex = app.config.globalProperties.bladeRoutes.findIndex(
156
+ (r: BladeInstanceConstructor) => r.name === routeName,
157
+ );
158
+ if (existingBladeRouteIndex !== -1) {
159
+ app.config.globalProperties.bladeRoutes.splice(existingBladeRouteIndex, 1);
160
+ }
161
+ }
162
+
163
+ // Add new bladeRoute
96
164
  app.config.globalProperties.bladeRoutes?.push({
97
165
  component: bladeVNode,
98
166
  route: page.url,
@@ -100,6 +168,13 @@ export const createAppModule = (
100
168
  isWorkspace: page.isWorkspace || false,
101
169
  });
102
170
 
171
+ // Register component globally
172
+ if (app.component(BladeInstanceConstructor.name)) {
173
+ // Overwrite existing component
174
+ console.warn(
175
+ `Component ${BladeInstanceConstructor.name} is already registered. It will be overwritten with the new component.`,
176
+ );
177
+ }
103
178
  app.component(BladeInstanceConstructor.name, BladeInstanceConstructor);
104
179
 
105
180
  // Add to menu
@@ -117,7 +192,16 @@ export const createAppModule = (
117
192
 
118
193
  if (notificationTemplates) {
119
194
  // Register notification templates
120
- Object.entries(notificationTemplates).forEach(([, template]) => {
195
+ Object.entries(notificationTemplates).forEach(([name, template]) => {
196
+ // Remove existing template if it exists
197
+ if (app.config.globalProperties.notificationTemplates) {
198
+ const existingIndex = app.config.globalProperties.notificationTemplates.findIndex(
199
+ (t: Component) => t.name === template.name,
200
+ );
201
+ if (existingIndex !== -1) {
202
+ app.config.globalProperties.notificationTemplates.splice(existingIndex, 1);
203
+ }
204
+ }
121
205
  app.config.globalProperties.notificationTemplates?.push(template);
122
206
  });
123
207
  }
@@ -125,12 +209,18 @@ export const createAppModule = (
125
209
  if (moduleComponents) {
126
210
  // Register module components globally
127
211
  Object.entries(moduleComponents).forEach(([name, component]) => {
212
+ // Check if the component is already registered
213
+ if (app.component(name)) {
214
+ // Overwrite existing component
215
+ console.warn(`Component ${name} is already registered. It will be overwritten with the new component.`);
216
+ }
128
217
  app.component(name, component);
129
218
  });
130
219
  }
131
220
 
132
221
  if (locales) {
133
222
  Object.entries(locales).forEach(([key, message]) => {
223
+ // Merge locale messages, overwriting existing ones
134
224
  i18n.global.mergeLocaleMessage(key, message);
135
225
  });
136
226
  }
@@ -3,10 +3,15 @@ export interface IUseLanguages {
3
3
  setLocale: (locale: string) => void;
4
4
  currentLocale: ComputedRef<string>;
5
5
  getLocaleByTag: (localeTag: string) => string | undefined;
6
+ resolveCamelCaseLocale: (locale: string) => string;
7
+ getFlag: (language: string) => Promise<string>;
6
8
  }
7
9
  export declare const useLanguages: () => {
8
10
  setLocale: (locale: string) => void;
9
11
  currentLocale: ComputedRef<string>;
10
12
  getLocaleByTag: (localeTag: string) => string | undefined;
13
+ resolveCamelCaseLocale: (locale: string) => string;
14
+ getFlag: (language: string) => Promise<string>;
15
+ getCountryCode: (language: string) => string;
11
16
  };
12
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useLanguages/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAY,MAAM,KAAK,CAAC;AAG5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC3D;AAED,eAAO,MAAM,YAAY;wBAcI,MAAM;;gCAWE,MAAM;CAazC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useLanguages/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAY,MAAM,KAAK,CAAC;AAI5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACnD,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD;AAED,eAAO,MAAM,YAAY;wBAcI,MAAM;;gCAWE,MAAM;qCAQD,MAAM;wBAeb,MAAM,KAAG,QAAQ,MAAM,CAAC;+BANvB,MAAM,KAAG,MAAM;CAmBjD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useSettings/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,GAAG,EAAO,WAAW,EAAa,MAAM,KAAK,CAAC;AAIzE,UAAU,UAAU;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClG;AAED,wBAAgB,WAAW,IAAI,YAAY,CAwC1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useSettings/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,GAAG,EAAO,WAAW,EAAa,MAAM,KAAK,CAAC;AAIzE,UAAU,UAAU;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClG;AAED,wBAAgB,WAAW,IAAI,YAAY,CAuC1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useTheme/index.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAGD,eAAO,MAAM,QAAQ,QAAO,SA8C3B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useTheme/index.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAGD,eAAO,MAAM,QAAQ,QAAO,SA+C3B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { ComputedRef } from "vue";
2
- import { UserDetail, SecurityResult, IdentityResult, LoginType, ExternalSignInProviderInfo, SignInResult } from "./../../api/platform";
2
+ import { UserDetail, SecurityResult, IdentityResult, LoginType, SignInResult } from "./../../api/platform";
3
3
  import { RequestPasswordResult } from "./../../types";
4
4
  interface IUseUser {
5
5
  user: ComputedRef<UserDetail | undefined>;
@@ -16,8 +16,6 @@ interface IUseUser {
16
16
  resetPasswordByToken: (userId: string, password: string, token: string) => Promise<SecurityResult>;
17
17
  requestPasswordReset: (loginOrEmail: string) => Promise<RequestPasswordResult>;
18
18
  changeUserPassword: (oldPassword: string, newPassword: string) => Promise<SecurityResult | undefined>;
19
- getExternalLoginProviders: () => Promise<ExternalSignInProviderInfo[] | undefined>;
20
- externalSignIn: (authenticationType?: string | undefined, returnUrl?: string | undefined) => void;
21
19
  getLoginType: () => Promise<LoginType[]>;
22
20
  isAuthenticated: ComputedRef<boolean>;
23
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useUser/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,WAAW,EAAE,MAAM,KAAK,CAAC;AACtD,OAAO,EACL,UAAU,EAGV,cAAc,EAEd,cAAc,EAGd,SAAS,EACT,0BAA0B,EAE1B,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAKtD,UAAU,QAAQ;IAChB,IAAI,EAAE,WAAW,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC9B,eAAe,EAAE,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAElD,QAAQ,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAC5G,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAChE,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACnG,oBAAoB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/E,kBAAkB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACtG,yBAAyB,EAAE,MAAM,OAAO,CAAC,0BAA0B,EAAE,GAAG,SAAS,CAAC,CAAC;IACnF,cAAc,EAAE,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAClG,YAAY,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACzC,eAAe,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACvC;AA+ND,eAAO,MAAM,OAAO,gBAA4C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/composables/useUser/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,WAAW,EAAE,MAAM,KAAK,CAAC;AACtD,OAAO,EACL,UAAU,EAGV,cAAc,EAEd,cAAc,EAEd,SAAS,EAET,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAItD,UAAU,QAAQ;IAChB,IAAI,EAAE,WAAW,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC9B,eAAe,EAAE,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAElD,QAAQ,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAC5G,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAChE,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACnG,oBAAoB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/E,kBAAkB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACtG,YAAY,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACzC,eAAe,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACvC;AA4JD,eAAO,MAAM,OAAO,gBAA4C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./locale";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../core/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Mapping ISO 639-1 language codes (and culture codes) to ISO 3166-1 alpha-2 country codes
3
+ * @see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
4
+ * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
5
+ */
6
+ export declare const languageToCountryMap: Record<string, string>;
7
+ //# sourceMappingURL=locale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../../core/constants/locale.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwEvD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../core/interceptors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIpC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,oGA8DlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../core/interceptors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIpC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,oGAiElD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAK,MAAM,KAAK,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAc,MAAM,qDAAqD,CAAC;AAK3G,eAAO,MAAM,YAAY;;aAAuE,OAAO;iBACxF,GAAG,GAAG,IAAI;CAavB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;iBAOX,GAAG,YAAY;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CA2GxD,CAAC;AAEF,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAK,MAAM,KAAK,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAc,MAAM,qDAAqD,CAAC;AAK3G,eAAO,MAAM,YAAY;;aAAuE,OAAO;iBACxF,GAAG,GAAG,IAAI;CAsBvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;iBAOX,GAAG,YAAY;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CA4LxD,CAAC;AAEF,cAAc,UAAU,CAAC"}