@lilaquadrat/design-core 0.1.0

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 (86) hide show
  1. package/README.md +236 -0
  2. package/html/index.html +25 -0
  3. package/html/index.mail.html +15 -0
  4. package/html/index.server.html +29 -0
  5. package/package.json +148 -0
  6. package/src/client-entry.ts +28 -0
  7. package/src/components/partials/action.partial.vue +30 -0
  8. package/src/components/partials/client-only.partial.vue +11 -0
  9. package/src/components/partials/error.partial.vue +73 -0
  10. package/src/components/partials/main-components.partial.vue +167 -0
  11. package/src/components/partials/mediadetection.partial.vue +71 -0
  12. package/src/components/partials/qrcode.partial.vue +35 -0
  13. package/src/customs.ts +12 -0
  14. package/src/env.d.ts +1 -0
  15. package/src/functions/PaymenyProviderFactory.ts +27 -0
  16. package/src/functions/lila-dom.ts +100 -0
  17. package/src/functions/shopify.provider.ts +378 -0
  18. package/src/functions/stripe.provider.ts +181 -0
  19. package/src/globals.d.ts +19 -0
  20. package/src/index.ts +47 -0
  21. package/src/interfaces/AppState.interface.ts +4 -0
  22. package/src/interfaces/EditorConfiguration.interface.ts +15 -0
  23. package/src/interfaces/EventDeclaration.interface.ts +19 -0
  24. package/src/interfaces/FrontendConfig.interface.ts +42 -0
  25. package/src/interfaces/GenericEvents.interface.ts +6 -0
  26. package/src/interfaces/GenericState.interface.ts +5 -0
  27. package/src/interfaces/IconsPartial.ts +9 -0
  28. package/src/interfaces/IdTokenExtended.interface.ts +7 -0
  29. package/src/interfaces/ModuleBaseProps.interface.ts +8 -0
  30. package/src/interfaces/PaymentProvider.interface.ts +13 -0
  31. package/src/libs/ActionNotice.ts +235 -0
  32. package/src/libs/Models.class.ts +482 -0
  33. package/src/main.ts +84 -0
  34. package/src/mixins/createCookieString.ts +78 -0
  35. package/src/mixins/createModelDeclaration.ts +29 -0
  36. package/src/mixins/createRouter.ts +30 -0
  37. package/src/mixins/date.ts +23 -0
  38. package/src/mixins/formatSize.ts +12 -0
  39. package/src/mixins/getAnchor.ts +14 -0
  40. package/src/mixins/getRoutes.ts +50 -0
  41. package/src/mixins/hasSlotContent.ts +32 -0
  42. package/src/mixins/hooks.ts +104 -0
  43. package/src/mixins/loadComponents.ts +107 -0
  44. package/src/mixins/logger.ts +32 -0
  45. package/src/mixins/replaceVariables.ts +19 -0
  46. package/src/mixins/scroll.ts +83 -0
  47. package/src/models/Address.model.ts +24 -0
  48. package/src/models/Contact.model.ts +22 -0
  49. package/src/models.ts +2 -0
  50. package/src/plugins/auth.ts +121 -0
  51. package/src/plugins/currency.ts +26 -0
  52. package/src/plugins/events.ts +156 -0
  53. package/src/plugins/filters.ts +43 -0
  54. package/src/plugins/inview.ts +351 -0
  55. package/src/plugins/replacer.ts +18 -0
  56. package/src/plugins/resize.ts +128 -0
  57. package/src/plugins/signupFlow.ts +89 -0
  58. package/src/plugins/traceable.ts +53 -0
  59. package/src/plugins/translations.ts +29 -0
  60. package/src/plugins/youtube.ts +80 -0
  61. package/src/routes.ts +132 -0
  62. package/src/server-entry.ts +113 -0
  63. package/src/stores/calls.store.ts +33 -0
  64. package/src/stores/cart.store.ts +186 -0
  65. package/src/stores/content.store.ts +83 -0
  66. package/src/stores/editor.store.ts +27 -0
  67. package/src/stores/files.store.ts +166 -0
  68. package/src/stores/main.store.ts +184 -0
  69. package/src/stores/user.store.ts +124 -0
  70. package/src/translations/de.ts +138 -0
  71. package/src/views/content.view.vue +219 -0
  72. package/src/views/download.view.vue +143 -0
  73. package/src/views/editor.view.vue +243 -0
  74. package/src/views/login.view.vue +82 -0
  75. package/src/views/signup-account.view.vue +64 -0
  76. package/tooling/cypress/config.ts +80 -0
  77. package/tooling/cypress/generate-manifest.js +5 -0
  78. package/tooling/cypress/manifest-utils.mjs +40 -0
  79. package/tooling/cypress/run-parallel.mjs +77 -0
  80. package/tooling/cypress/support/commands.ts +436 -0
  81. package/tooling/cypress/support/e2e.ts +17 -0
  82. package/tooling/eslint.config.js +223 -0
  83. package/tooling/preview.plugin.ts +134 -0
  84. package/tooling/ssr-test/index.mjs +391 -0
  85. package/tooling/stylelint.config.mjs +24 -0
  86. package/tooling/vite.ts +246 -0
@@ -0,0 +1,23 @@
1
+ import dayjs from 'dayjs';
2
+ import utc from 'dayjs/plugin/utc';
3
+ import timezone from 'dayjs/plugin/timezone';
4
+ import relativeTime from 'dayjs/plugin/relativeTime';
5
+ import 'dayjs/locale/de';
6
+
7
+ // Initialize dayjs plugins
8
+ dayjs.extend(utc);
9
+ dayjs.extend(timezone);
10
+ dayjs.extend(relativeTime);
11
+ dayjs.locale('de');
12
+
13
+ /**
14
+ * Returns a dayjs instance with the timezone set to Berlin
15
+ *
16
+ * @param date Optional date parameter to initialize the dayjs instance with
17
+ * @returns A dayjs instance with Berlin timezone
18
+ */
19
+ export function useDate (date?: string | number | Date | dayjs.Dayjs) {
20
+ return dayjs(date).tz('Europe/Berlin');
21
+ }
22
+
23
+ export default useDate;
@@ -0,0 +1,12 @@
1
+ export function formatSize (size?: number): string {
2
+
3
+ if (!size) return '';
4
+
5
+ if (size < 1024) return `${size} B`;
6
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
7
+
8
+ return `${(size / 1024 / 1024).toFixed(1)} MB`;
9
+
10
+ }
11
+
12
+ export default formatSize;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Converts the input string to a URL-friendly format.
3
+ *
4
+ * The function performs the following operations in order:
5
+ * 1. Converts all characters in the input string to lowercase.
6
+ * 2. Removes all non-word characters (anything except alphanumeric and underscores),
7
+ * whitespace, and hyphens.
8
+ * 3. Trims leading and trailing spaces from the string.
9
+ * 4. Replaces one or more spaces with a single hyphen.
10
+ * 5. Replaces multiple consecutive hyphens with a single hyphen.
11
+ *
12
+ */
13
+ export default (input: string) => input?.toLowerCase().replace(/[^\w\s-]/g, '').trim().replace(/\s+/g, '-')
14
+ .replace(/-+/g, '-');
@@ -0,0 +1,50 @@
1
+ import type { RouteRecordRaw } from 'vue-router';
2
+ import logger from './logger';
3
+
4
+ export interface RouteSets {
5
+ dynamicRoutes: readonly RouteRecordRaw[]
6
+ editorRoutes: readonly RouteRecordRaw[]
7
+ previewRoutes: readonly RouteRecordRaw[]
8
+ }
9
+
10
+ export default (routeSets: RouteSets) => {
11
+
12
+ const config = __FRONTEND_CONFIG__;
13
+ const mode = import.meta.env.MODE;
14
+ const isIframe = window !== window.top;
15
+ const isDynamic = config.dynamic;
16
+
17
+ /**
18
+ * the shareable static build (yarn build:preview) is always the module
19
+ * gallery - it has no backend and no editor parent to talk to
20
+ */
21
+ if(__PREVIEW__) {
22
+
23
+ logger.router('mode: preview routes (static build)');
24
+ return routeSets.previewRoutes;
25
+
26
+ }
27
+
28
+ /**
29
+ * running in an iframe indicates that we are in the STUDIO editor
30
+ */
31
+ if(isIframe) {
32
+
33
+ logger.router('mode: editor routes');
34
+ return routeSets.editorRoutes;
35
+ }
36
+
37
+ /**
38
+ * no iframe but development mode indicates that the design is started locally
39
+ */
40
+ if(mode === 'development' && !isDynamic) {
41
+
42
+ logger.router('mode: preview routes');
43
+ return routeSets.previewRoutes
44
+
45
+ }
46
+
47
+ logger.router('mode: dynamic routes');
48
+ return routeSets.dynamicRoutes;
49
+
50
+ }
@@ -0,0 +1,32 @@
1
+ // slotUtils.ts
2
+ import type { Slot, VNode } from 'vue';
3
+ import { Comment } from 'vue';
4
+
5
+ /**
6
+ * Checks whether the provided slot function returns any meaningful content.
7
+ *
8
+ * @param slot - The slot function to check.
9
+ * @returns True if the slot has meaningful content, false otherwise.
10
+ */
11
+ export function hasSlotContent (slot: Slot | undefined): boolean {
12
+ // If the slot function does not exist, consider the slot empty.
13
+ if (!slot) return false;
14
+
15
+ // Call the slot function to retrieve an array of VNodes.
16
+ const nodes: VNode[] = slot();
17
+
18
+ if (!nodes || nodes.length === 0) return false;
19
+
20
+ // Check if any of the VNodes contain meaningful content.
21
+ return nodes.some((node: VNode) => {
22
+ // Ignore comment nodes.
23
+ if (node.type === Comment) return false;
24
+
25
+ // If the node's children is a string, ignore it if it’s only whitespace.
26
+ if (typeof node.children === 'string' && node.children.trim() === '') {
27
+ return false;
28
+ }
29
+
30
+ return true;
31
+ });
32
+ }
@@ -0,0 +1,104 @@
1
+ import { useMainStore } from '../stores/main.store'
2
+ import type { Router } from 'vue-router';
3
+ import logger from './logger';
4
+ import { auth } from '../plugins/auth';
5
+ import useUserStore from '../stores/user.store';
6
+ import { routeForLock } from '../plugins/signupFlow';
7
+
8
+ export default (router: Router, options?: {initAuth?: boolean}, target: 'server' | 'client' = 'server') => {
9
+
10
+ router.beforeEach(async (to, from, next) => {
11
+
12
+ const mainStore = useMainStore();
13
+ const userStore = useUserStore();
14
+
15
+ if (!mainStore.startupDone) {
16
+
17
+ logger.startup('start');
18
+
19
+ if(!mainStore.config) {
20
+ mainStore.config = __FRONTEND_CONFIG__;
21
+ }
22
+
23
+ if(options?.initAuth !== false && !mainStore.isEditor){
24
+ if (mainStore.config?.auth0Options) {
25
+
26
+ await auth.init(mainStore.config.auth0Options);
27
+
28
+ }
29
+
30
+ await userStore.initCustomer();
31
+ }
32
+
33
+ mainStore.startupDone = true;
34
+
35
+ logger.startup('done');
36
+
37
+ } else {
38
+
39
+ if(target === 'client') {
40
+
41
+ if(!mainStore.clientOnlyStartupDone) {
42
+
43
+ if(options?.initAuth !== false) {
44
+
45
+ if (mainStore.config?.auth0Options) {
46
+
47
+ await auth.init(mainStore.config.auth0Options);
48
+
49
+ }
50
+
51
+ await userStore.initCustomer();
52
+ }
53
+
54
+ mainStore.clientOnlyStartupDone = true;
55
+
56
+ }
57
+
58
+ }
59
+
60
+ }
61
+
62
+ if(mainStore.disabledLockUpdate) mainStore.disabledLockUpdate = false;
63
+
64
+ /**
65
+ * signup gate: locked users are forced through the signup steps before they can
66
+ * reach any members area. public pages stay reachable.
67
+ */
68
+ const locked = userStore.locked;
69
+ const toName = typeof to.name === 'string' ? to.name : '';
70
+
71
+ if (locked) {
72
+
73
+ const stepRoute = routeForLock(locked);
74
+
75
+ // a locked user heading into members content (that isn't a signup step) → current step
76
+ if (to.meta?.contentType === 'members' && !toName.startsWith('signup') && toName !== stepRoute.name) {
77
+
78
+ return next(stepRoute);
79
+
80
+ }
81
+
82
+ // a locked user on any signup screen that isn't their current step → that step
83
+ // (signup-account is the external account-creation trigger, leave it alone)
84
+ if (toName.startsWith('signup') && toName !== 'signup-account' && toName !== stepRoute.name) {
85
+
86
+ return next(stepRoute);
87
+
88
+ }
89
+
90
+ } else if (toName === 'signup' || toName === 'signup-connect' || toName === 'signup-verify') {
91
+
92
+ // not locked: a fully set up (authenticated) user has nothing to do here → app
93
+ if (auth.isAuth.value) return next({ name: 'members' });
94
+
95
+ // a guest can only start at the register step
96
+ if (toName !== 'signup') return next({ name: 'signup' });
97
+
98
+ }
99
+
100
+ next();
101
+
102
+ });
103
+
104
+ };
@@ -0,0 +1,107 @@
1
+ import { defineAsyncComponent, type App, type Component, type AsyncComponentLoader } from 'vue';
2
+
3
+ function getName (name: string, type: 'module' | 'partial', namespace?: string) {
4
+
5
+ return namespace ? `${namespace}-${name}-${type}` : `${name}-${type}`;
6
+
7
+ }
8
+
9
+ function loadViaGlob (components: Record<string, Record<'default', Component>>, namespace: string | undefined, app: App<Element>) {
10
+
11
+ Object.entries(components).forEach(([filename, definition]) => {
12
+
13
+ // Get name of component, based on filename
14
+ // "./components/Fruits.vue" will become "Fruits"
15
+ const matches = filename.match(/([a-z-]+)\.(module|partial).(vue|ts)$/i);
16
+
17
+ if (matches) {
18
+
19
+ const componentName = namespace ? `${namespace}-${matches[1]}-${matches[2]}` : `${matches[1]}-${matches[2]}`;
20
+
21
+ // Register component on this Vue instance
22
+ app.component(componentName, definition.default);
23
+ }
24
+ });
25
+
26
+ }
27
+
28
+ function loadViaDeclaration (components: { name: string, component: AsyncComponentLoader<Component> }[], namespace: string | undefined, type: 'module' | 'partial', app: App<Element>) {
29
+
30
+ components.forEach((single) => {
31
+
32
+ const name = getName(single.name, type, namespace);
33
+
34
+ app.component(name, defineAsyncComponent(single.component));
35
+
36
+ });
37
+
38
+ }
39
+
40
+ function loadViaDeclarationSync (components: { name: string, component: Component }[], namespace: string | undefined, type: 'module' | 'partial', app: App<Element>) {
41
+
42
+ return components.map(async (single) => {
43
+
44
+ const name = getName(single.name, type, namespace);
45
+
46
+ app.component(name, single.component);
47
+
48
+ });
49
+
50
+ }
51
+
52
+ function getAvailableModules (modules: any[]) {
53
+
54
+ return modules.filter((single) => single.availableInEditor !== false).map((single) => ({
55
+ name : `${single.name}-module`,
56
+ variants : single.variants || [],
57
+ editor : single.editor || {},
58
+ additionalData: single.additionalData || {}
59
+ }));
60
+
61
+ }
62
+
63
+ function createModulesArray (modules: any[]) {
64
+
65
+ const modulesArray: any[] = [];
66
+
67
+ modules.forEach((singleModule) => {
68
+
69
+ if (!singleModule.target) return;
70
+
71
+ const foundModule = modulesArray.find((single) => single.name === singleModule.name);
72
+ const useModule = foundModule
73
+ ? foundModule
74
+ : {
75
+ name : singleModule.name,
76
+ targets: {}
77
+ }
78
+
79
+ singleModule.target.forEach((target: string) => {
80
+
81
+ useModule.targets[target] = {
82
+ variants : singleModule.variants,
83
+ components: singleModule.component,
84
+ name : singleModule.name
85
+ };
86
+
87
+ });
88
+
89
+ if (!foundModule) {
90
+
91
+ modulesArray.push(useModule);
92
+
93
+ }
94
+
95
+ });
96
+
97
+ return modulesArray;
98
+
99
+ }
100
+
101
+ export {
102
+ loadViaGlob,
103
+ loadViaDeclaration,
104
+ loadViaDeclarationSync,
105
+ getAvailableModules,
106
+ createModulesArray,
107
+ }
@@ -0,0 +1,32 @@
1
+ interface LoggerFunction {
2
+ (message: string, color?: string): void;
3
+ }
4
+
5
+ interface Logger {
6
+ [key: string]: LoggerFunction;
7
+ }
8
+
9
+ // Predefined colors for categories, if needed
10
+ const categoryColors: { [key: string]: string } = {
11
+ plugins: 'background-color: #3f2d56; color: #FFF',
12
+ startup: 'background-color: #5A8C99; color: #FFF',
13
+ auth : 'background-color: #5A8C99; color: #FFF',
14
+ // Add other predefined categories and colors as needed
15
+ };
16
+ const logger = new Proxy<Logger>({}, {
17
+
18
+ get: function (target, propKey, receiver) {
19
+ // Method to log messages with dynamic category-based styling and optional color for the category
20
+ const log = (category: string, message: string, color?: string): void => {
21
+ const categoryStyle = color ? `color: ${color}` : categoryColors[category.toLowerCase()] || 'color: #FFF';
22
+
23
+ // Log the category with style and message without style
24
+
25
+ console.log(`%c[FRONTEND]%c%c[${category.toUpperCase()}]%c ${message}`, 'background-color: #3f2d56;', 'padding: 5px', categoryStyle, 'color: inherit');
26
+ };
27
+
28
+ return (message: string, color?: string) => log(propKey.toString(), message, color);
29
+ }
30
+ });
31
+
32
+ export default logger;
@@ -0,0 +1,19 @@
1
+ import type { Customers, List, Me } from '@lilaquadrat/interfaces';
2
+ import objectPath from 'object-path';
3
+ import useDate from './date';
4
+ import 'dayjs/locale/de';
5
+
6
+ const dateVariables = ['list.start', 'list.end'];
7
+
8
+ export default (template?: string, variables?: Record<string, string | undefined | Record<string, string> | Customers | Me | List>) => template?.replace(/\$([\w.]+)/g, (match, name) => {
9
+ // Check if the placeholder's name exists in the variables object
10
+ if(!variables) return '';
11
+
12
+ const returnString = objectPath.get(variables as Record<string, string>, name);
13
+
14
+ if(!returnString) return '';
15
+
16
+ if(dateVariables.includes(name)) return useDate(returnString).locale('de').format('DD.MM.YYYY HH:mm');
17
+
18
+ return returnString;
19
+ });
@@ -0,0 +1,83 @@
1
+ import type { RouteLocationNormalized, RouterScrollBehavior } from 'vue-router';
2
+
3
+ interface ScrollPosition {
4
+ left: number;
5
+ top: number;
6
+ behavior?: ScrollBehavior;
7
+ }
8
+
9
+ // Add this constant at the top of the file, after the interfaces
10
+ const GENERAL_OFFSET = 20;
11
+
12
+ // Helper function to calculate the total height of fixed or overlaying elements.
13
+ function getFixedElementsOffset (): number {
14
+ const selectors: string[] = ['.anchor-calculation'];
15
+
16
+ return selectors.reduce((total, selector) => {
17
+ const el = document.querySelector(selector) as HTMLElement | null;
18
+
19
+ return el ? total + el.offsetHeight : total;
20
+ }, 0);
21
+ }
22
+
23
+ /**
24
+ * scroll into view with calulated offset from fixed overlay elements
25
+ */
26
+ export function scrollToSelector (selector: string): Promise<void> {
27
+ return new Promise((resolve) => {
28
+ const targetEl = document.querySelector(selector) as HTMLElement | null;
29
+
30
+ if (targetEl) {
31
+ const offset = getFixedElementsOffset();
32
+ const elementTop = targetEl.getBoundingClientRect().top + window.scrollY;
33
+
34
+ window.scrollTo({
35
+ left : 0,
36
+ top : elementTop - offset - GENERAL_OFFSET,
37
+ behavior: 'smooth'
38
+ });
39
+ }
40
+
41
+ resolve();
42
+ });
43
+ }
44
+ /**
45
+ * scroll into view with calulated offset from fixed overlay elements
46
+ * usage for vue-router scrollBehavior
47
+ */
48
+ export const smartScrollBehavior: RouterScrollBehavior = (
49
+ to: RouteLocationNormalized,
50
+ _from: RouteLocationNormalized,
51
+ savedPosition: ScrollPosition | null | undefined
52
+ ): ReturnType<RouterScrollBehavior> => {
53
+ if (savedPosition) {
54
+ return savedPosition;
55
+ }
56
+
57
+ if (to.hash) {
58
+
59
+ return new Promise((resolve) => {
60
+
61
+ const targetEl = document.querySelector(to.hash) as HTMLElement | null;
62
+
63
+ if (targetEl) {
64
+
65
+ const offset = getFixedElementsOffset();
66
+ const elementTop = targetEl.getBoundingClientRect().top + window.scrollY;
67
+
68
+ resolve({
69
+ left : 0,
70
+ top : elementTop - offset - GENERAL_OFFSET,
71
+ behavior: 'smooth'
72
+ });
73
+
74
+ } else {
75
+ resolve({ left: 0, top: 0 });
76
+ }
77
+
78
+ });
79
+
80
+ }
81
+
82
+ return { left: 0, top: 0 };
83
+ };
@@ -0,0 +1,24 @@
1
+
2
+ import Models, { type ModelDeclaration } from '../libs/Models.class';
3
+
4
+ export default interface Address {
5
+ osm_id: number
6
+ street: string
7
+ streetNumber: string
8
+ city: string
9
+ zipcode: string
10
+ country: string
11
+ addressAddition: string
12
+ }
13
+
14
+ const declaration: ModelDeclaration<Address> = {
15
+ osm_id : { type: 'number' },
16
+ street : { type: 'string' },
17
+ streetNumber : { type: 'string' },
18
+ city : { type: 'string' },
19
+ zipcode : { type: 'string' },
20
+ country : { type: 'string' },
21
+ addressAddition: { type: 'string' },
22
+ };
23
+
24
+ Models.register('address', declaration);
@@ -0,0 +1,22 @@
1
+
2
+ import Models, { type ModelDeclaration } from '../libs/Models.class';
3
+ import type { Contact } from '@lilaquadrat/interfaces';
4
+
5
+ const declaration: ModelDeclaration<Omit<Contact, 'emailConfirmed' | 'emailConfirmationCode'>> = {
6
+ prename : { type: 'string' },
7
+ name : { type: 'string' },
8
+ email : { type: 'string' },
9
+ phone : { type: 'string' },
10
+ message : { type: 'string' },
11
+ type : { type: 'string' },
12
+ category : { type: 'string' },
13
+ osm_id : { type: 'number' },
14
+ street : { type: 'string' },
15
+ streetNumber : { type: 'string' },
16
+ city : { type: 'string' },
17
+ zipcode : { type: 'string' },
18
+ country : { type: 'string' },
19
+ addressAddition: { type: 'string' },
20
+ };
21
+
22
+ Models.register('contact', declaration);
package/src/models.ts ADDED
@@ -0,0 +1,2 @@
1
+ import './models/Contact.model';
2
+ import './models/Address.model';
@@ -0,0 +1,121 @@
1
+ import type AppState from '../interfaces/AppState.interface';
2
+ import type IdTokenExtended from '../interfaces/IdTokenExtended.interface';
3
+ import logger from '../mixins/logger';
4
+ import { Auth0Client, type Auth0ClientOptions } from '@auth0/auth0-spa-js';
5
+ import { ref } from 'vue';
6
+
7
+ class Auth {
8
+
9
+ auth0!: Auth0Client;
10
+
11
+ options: { dev: boolean } = { dev: false };
12
+
13
+ isAuth = ref<boolean>(false);
14
+
15
+ token = ref<string>();
16
+
17
+ returnTo?: string;
18
+
19
+ async init (auth0: Auth0ClientOptions, options?: { dev: boolean }) {
20
+
21
+ logger.auth('init')
22
+ this.auth0 = new Auth0Client(auth0);
23
+
24
+ const redirectUri = auth0.authorizationParams?.redirect_uri;
25
+
26
+ if (redirectUri) this.returnTo = new URL(redirectUri).origin;
27
+
28
+ if (options) this.options = options;
29
+
30
+ try {
31
+
32
+ await this.updateAuthStatus();
33
+
34
+ } catch (error) {
35
+
36
+ console.error(error);
37
+
38
+ }
39
+
40
+ try {
41
+
42
+ if (this.isAuth.value && !this.token.value) await this.getToken();
43
+
44
+ } catch (error) {
45
+
46
+ console.error(error);
47
+
48
+ }
49
+
50
+ logger.auth('init done')
51
+ }
52
+
53
+ async updateAuthStatus () {
54
+
55
+ this.isAuth.value = await this.auth0.isAuthenticated();
56
+
57
+ }
58
+
59
+ async getToken () {
60
+
61
+ this.token.value = await this.auth0.getTokenSilently({ authorizationParams: { scope: 'openid profile email offline_access' } });
62
+
63
+ }
64
+
65
+ async refreshToken () {
66
+
67
+ this.token.value = await this.auth0.getTokenSilently({ authorizationParams: { scope: 'openid profile email offline_access' } });
68
+
69
+ }
70
+
71
+ getTokenContent (): Promise<IdTokenExtended | undefined> {
72
+
73
+ return this.auth0.getIdTokenClaims() as Promise<IdTokenExtended | undefined>;
74
+
75
+ }
76
+
77
+ triggerLogin (customerId?: string, emailConfirmationCode?: string) {
78
+
79
+ this.auth0.loginWithRedirect<AppState>({ appState: { customerId, emailConfirmationCode } });
80
+
81
+ }
82
+
83
+ triggerSignup (customerId?: string, emailConfirmationCode?: string) {
84
+
85
+ this.auth0.loginWithRedirect<AppState>({ appState: { customerId, emailConfirmationCode }, authorizationParams: { screen_hint: 'signup' } });
86
+
87
+ }
88
+
89
+ triggerLogout () {
90
+
91
+ this.auth0.logout(this.returnTo ? { logoutParams: { returnTo: this.returnTo } } : undefined);
92
+ localStorage.removeItem('lila-customer');
93
+
94
+ }
95
+
96
+ async handleCallback () {
97
+
98
+ const result = await this.auth0.handleRedirectCallback<AppState>();
99
+
100
+ await this.updateAuthStatus();
101
+ await this.getToken();
102
+
103
+ return result;
104
+
105
+ }
106
+
107
+ }
108
+
109
+ const auth = new Auth;
110
+ const plugin = {
111
+ install: (): void => {
112
+ logger.plugins('auth installed')
113
+ },
114
+ };
115
+ const useAuth = () => ({ authToken: auth.token.value })
116
+
117
+ export default plugin;
118
+ export {
119
+ auth,
120
+ useAuth
121
+ }
@@ -0,0 +1,26 @@
1
+ import logger from '../mixins/logger';
2
+ import type { App } from 'vue';
3
+
4
+ function formatCurrency (amount: number) {
5
+
6
+ return new Intl.NumberFormat(navigator?.language || 'en-US', {
7
+ style : 'currency',
8
+ currency: 'EUR',
9
+ }).format(amount);
10
+
11
+ }
12
+
13
+ const plugin = {
14
+ install: (vue: App): void => {
15
+
16
+ vue.config.globalProperties.$currency = (amount: number) => formatCurrency(amount);
17
+
18
+ logger.plugins('currency installed');
19
+
20
+ },
21
+ };
22
+
23
+ export default plugin;
24
+ export {
25
+ formatCurrency
26
+ }