axitech-widget 0.0.2

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 (147) hide show
  1. package/README.md +203 -0
  2. package/dist/Modal-fab2abfa.mjs +4 -0
  3. package/dist/axitech-widget.es.js +7 -0
  4. package/dist/axitech-widget.iife.js +698 -0
  5. package/dist/axitech-widget.umd.js +698 -0
  6. package/dist/index-2839010c.mjs +30306 -0
  7. package/dist/style.css +1 -0
  8. package/dist/types/components/base/ActionCard.vue.d.ts +36 -0
  9. package/dist/types/components/base/Button.vue.d.ts +9 -0
  10. package/dist/types/components/base/Collapse.vue.d.ts +2 -0
  11. package/dist/types/components/base/DatePicker.vue.d.ts +70 -0
  12. package/dist/types/components/base/GoogleAutocomplete.vue.d.ts +23 -0
  13. package/dist/types/components/base/GoogleMap.vue.d.ts +26 -0
  14. package/dist/types/components/base/Input.vue.d.ts +43 -0
  15. package/dist/types/components/base/Modal.vue.d.ts +55 -0
  16. package/dist/types/components/base/SelectButton.vue.d.ts +56 -0
  17. package/dist/types/components/base/Spinner.vue.d.ts +34 -0
  18. package/dist/types/components/base/uploader/ImageUpload.vue.d.ts +23 -0
  19. package/dist/types/components/base/uploader/Upload.vue.d.ts +4 -0
  20. package/dist/types/components/claims-widget/ClaimsWidget.ce.vue.d.ts +2 -0
  21. package/dist/types/components/claims-widget/components/Error.vue.d.ts +2 -0
  22. package/dist/types/components/claims-widget/components/SkeletonLoader.vue.d.ts +56 -0
  23. package/dist/types/components/claims-widget/components/StepConfirm.vue.d.ts +6 -0
  24. package/dist/types/components/claims-widget/components/StepSelector.vue.d.ts +6 -0
  25. package/dist/types/components/claims-widget/components/accident/Accident.vue.d.ts +8 -0
  26. package/dist/types/components/claims-widget/components/accident/ImageSection.vue.d.ts +29 -0
  27. package/dist/types/components/claims-widget/components/accident/MapSection.vue.d.ts +29 -0
  28. package/dist/types/components/claims-widget/components/accident/NoteSection.vue.d.ts +21 -0
  29. package/dist/types/components/claims-widget/components/accident/SectionWrapper.vue.d.ts +23 -0
  30. package/dist/types/components/claims-widget/components/accident/YourVehicle.vue.d.ts +2 -0
  31. package/dist/types/components/claims-widget/components/fireWater/FireWater.vue.d.ts +6 -0
  32. package/dist/types/components/claims-widget/components/illustration/Accident.vue.d.ts +2 -0
  33. package/dist/types/components/claims-widget/components/illustration/Location.vue.d.ts +2 -0
  34. package/dist/types/components/claims-widget/components/illustration/SubmittedSuccessfully.vue.d.ts +2 -0
  35. package/dist/types/components/claims-widget/components/illustration/Unsupported.vue.d.ts +2 -0
  36. package/dist/types/components/claims-widget/components/windscreen/Windscreen.vue.d.ts +6 -0
  37. package/dist/types/components/claims-widget/components/windscreen/WindscreenLoading.vue.d.ts +2 -0
  38. package/dist/types/components/index.d.ts +2 -0
  39. package/dist/types/composables/googleMaps.d.ts +11 -0
  40. package/dist/types/constants/index.d.ts +1 -0
  41. package/dist/types/index.d.ts +34 -0
  42. package/dist/types/plugins/firebase.d.ts +2 -0
  43. package/dist/types/plugins/firebaseCloudStorage.d.ts +16 -0
  44. package/dist/types/utils/autocomplete.d.ts +2 -0
  45. package/dist/types/utils/colorHelpers.d.ts +7 -0
  46. package/dist/types/utils/config.d.ts +131 -0
  47. package/dist/types/utils/customElementRegister.d.ts +1 -0
  48. package/dist/types/utils/fontUtils.d.ts +1 -0
  49. package/dist/types/utils/index.d.ts +141 -0
  50. package/dist/types/utils/map.d.ts +21 -0
  51. package/dist/types/utils/request.d.ts +30 -0
  52. package/dist/types/utils/svgUtils.d.ts +0 -0
  53. package/dist/types/utils/themeUtils.d.ts +9 -0
  54. package/package.json +77 -0
  55. package/src/assets/fonts/Volvo Novum-Italic.eot +0 -0
  56. package/src/assets/fonts/Volvo Novum-Italic.svg +723 -0
  57. package/src/assets/fonts/Volvo Novum-Italic.woff +0 -0
  58. package/src/assets/fonts/Volvo Novum-Italic.woff2 +0 -0
  59. package/src/assets/fonts/Volvo Novum-Light.eot +0 -0
  60. package/src/assets/fonts/Volvo Novum-Light.svg +707 -0
  61. package/src/assets/fonts/Volvo Novum-Light.woff +0 -0
  62. package/src/assets/fonts/Volvo Novum-Light.woff2 +0 -0
  63. package/src/assets/fonts/Volvo Novum-LightItalic.eot +0 -0
  64. package/src/assets/fonts/Volvo Novum-LightItalic.svg +723 -0
  65. package/src/assets/fonts/Volvo Novum-LightItalic.woff +0 -0
  66. package/src/assets/fonts/Volvo Novum-LightItalic.woff2 +0 -0
  67. package/src/assets/fonts/Volvo Novum-Medium.eot +0 -0
  68. package/src/assets/fonts/Volvo Novum-Medium.svg +706 -0
  69. package/src/assets/fonts/Volvo Novum-Medium.woff +0 -0
  70. package/src/assets/fonts/Volvo Novum-Medium.woff2 +0 -0
  71. package/src/assets/fonts/Volvo Novum-MediumItalic.eot +0 -0
  72. package/src/assets/fonts/Volvo Novum-MediumItalic.svg +724 -0
  73. package/src/assets/fonts/Volvo Novum-MediumItalic.woff +0 -0
  74. package/src/assets/fonts/Volvo Novum-MediumItalic.woff2 +0 -0
  75. package/src/assets/fonts/Volvo Novum-Regular.eot +0 -0
  76. package/src/assets/fonts/Volvo Novum-Regular.svg +707 -0
  77. package/src/assets/fonts/Volvo Novum-Regular.woff +0 -0
  78. package/src/assets/fonts/Volvo Novum-Regular.woff2 +0 -0
  79. package/src/assets/fonts/Volvo Novum-SemiLight.eot +0 -0
  80. package/src/assets/fonts/Volvo Novum-SemiLight.svg +706 -0
  81. package/src/assets/fonts/Volvo Novum-SemiLight.woff +0 -0
  82. package/src/assets/fonts/Volvo Novum-SemiLight.woff2 +0 -0
  83. package/src/assets/fonts/Volvo Novum-SemiLightItalic.eot +0 -0
  84. package/src/assets/fonts/Volvo Novum-SemiLightItalic.svg +722 -0
  85. package/src/assets/fonts/Volvo Novum-SemiLightItalic.woff +0 -0
  86. package/src/assets/fonts/Volvo Novum-SemiLightItalic.woff2 +0 -0
  87. package/src/assets/styles/collapse.sass +20 -0
  88. package/src/assets/styles/datepicker.sass +14 -0
  89. package/src/assets/styles/main.sass +182 -0
  90. package/src/assets/styles/map.sass +41 -0
  91. package/src/assets/styles/togglebutton.sass +4 -0
  92. package/src/components/base/ActionCard.vue +49 -0
  93. package/src/components/base/Button.vue +16 -0
  94. package/src/components/base/Collapse.vue +41 -0
  95. package/src/components/base/DatePicker.vue +94 -0
  96. package/src/components/base/GoogleAutocomplete.vue +116 -0
  97. package/src/components/base/GoogleMap.vue +114 -0
  98. package/src/components/base/Input.vue +44 -0
  99. package/src/components/base/Modal.vue +117 -0
  100. package/src/components/base/SelectButton.vue +67 -0
  101. package/src/components/base/Spinner.vue +39 -0
  102. package/src/components/base/uploader/ImageUpload.vue +85 -0
  103. package/src/components/base/uploader/Upload.vue +14 -0
  104. package/src/components/claims-widget/ClaimsWidget.ce.vue +117 -0
  105. package/src/components/claims-widget/assets/accident.svg +181 -0
  106. package/src/components/claims-widget/assets/car.svg +6 -0
  107. package/src/components/claims-widget/assets/currentlocation.svg +3 -0
  108. package/src/components/claims-widget/assets/door.svg +3 -0
  109. package/src/components/claims-widget/assets/location.svg +20 -0
  110. package/src/components/claims-widget/assets/marker.svg +1 -0
  111. package/src/components/claims-widget/assets/unsupported.svg +107 -0
  112. package/src/components/claims-widget/assets/windscreen.svg +3 -0
  113. package/src/components/claims-widget/assets/zoomin.svg +3 -0
  114. package/src/components/claims-widget/assets/zoomout.svg +3 -0
  115. package/src/components/claims-widget/components/Error.vue +21 -0
  116. package/src/components/claims-widget/components/SkeletonLoader.vue +77 -0
  117. package/src/components/claims-widget/components/StepConfirm.vue +85 -0
  118. package/src/components/claims-widget/components/StepSelector.vue +58 -0
  119. package/src/components/claims-widget/components/accident/Accident.vue +267 -0
  120. package/src/components/claims-widget/components/accident/ImageSection.vue +82 -0
  121. package/src/components/claims-widget/components/accident/MapSection.vue +72 -0
  122. package/src/components/claims-widget/components/accident/NoteSection.vue +83 -0
  123. package/src/components/claims-widget/components/accident/SectionWrapper.vue +16 -0
  124. package/src/components/claims-widget/components/accident/YourVehicle.vue +60 -0
  125. package/src/components/claims-widget/components/fireWater/FireWater.vue +42 -0
  126. package/src/components/claims-widget/components/illustration/Accident.vue +735 -0
  127. package/src/components/claims-widget/components/illustration/Location.vue +74 -0
  128. package/src/components/claims-widget/components/illustration/SubmittedSuccessfully.vue +2270 -0
  129. package/src/components/claims-widget/components/illustration/Unsupported.vue +393 -0
  130. package/src/components/claims-widget/components/windscreen/Windscreen.vue +44 -0
  131. package/src/components/claims-widget/components/windscreen/WindscreenLoading.vue +34 -0
  132. package/src/components/index.ts +3 -0
  133. package/src/composables/googleMaps.ts +157 -0
  134. package/src/constants/index.ts +1 -0
  135. package/src/index.ts +76 -0
  136. package/src/plugins/firebase.ts +18 -0
  137. package/src/plugins/firebaseCloudStorage.ts +88 -0
  138. package/src/utils/autocomplete.ts +57 -0
  139. package/src/utils/colorHelpers.ts +96 -0
  140. package/src/utils/config.ts +140 -0
  141. package/src/utils/customElementRegister.ts +40 -0
  142. package/src/utils/fontUtils.ts +24 -0
  143. package/src/utils/index.ts +5 -0
  144. package/src/utils/map.ts +212 -0
  145. package/src/utils/request.ts +76 -0
  146. package/src/utils/svgUtils.ts +0 -0
  147. package/src/utils/themeUtils.ts +40 -0
@@ -0,0 +1,157 @@
1
+ import { Loader } from '@googlemaps/js-api-loader';
2
+ import { Ref, ref } from 'vue';
3
+ import {
4
+ ILocationInfo,
5
+ addYourLocationButton,
6
+ getLocationInfo,
7
+ getLocation as getLocationRaw,
8
+ zoomControl,
9
+ } from 'src/utils/map';
10
+
11
+ const loader = new Loader({
12
+ apiKey: process.env.VITE_APP_GMAPS_KEY as string,
13
+ version: 'quarterly',
14
+ libraries: ['places'],
15
+ });
16
+ const loadPromise = loader.load();
17
+
18
+ export function useGoogleMaps(
19
+ componentOrSelector: string | Ref<HTMLElement | undefined> | HTMLElement,
20
+ onPlaceChanged?: (place: ILocationInfo) => void
21
+ ) {
22
+ const map = ref<google.maps.Map>();
23
+ const marker = ref<google.maps.Marker>();
24
+ const isMapsLoaded = ref(false);
25
+ const defaultLocation = { latitude: 51.51160940068564, longitude: -0.08719166730719596 };
26
+ const isGeolocationAllowed = ref(false);
27
+ const defaultZoom = ref(16);
28
+
29
+ const getElement = () => {
30
+ const error = new Error('Can not load map, no element or selector provided');
31
+ if (!componentOrSelector) {
32
+ throw error;
33
+ }
34
+
35
+ if (typeof componentOrSelector === 'string') {
36
+ const elem = document.getElementById(componentOrSelector);
37
+ if (!elem) {
38
+ throw error;
39
+ }
40
+ return elem;
41
+ }
42
+
43
+ if (componentOrSelector instanceof HTMLElement) {
44
+ return componentOrSelector;
45
+ }
46
+
47
+ if (!componentOrSelector.value) {
48
+ throw error;
49
+ }
50
+ return componentOrSelector.value;
51
+ };
52
+
53
+ async function waitComplete() {
54
+ await loadPromise;
55
+ isMapsLoaded.value = true;
56
+ }
57
+ waitComplete();
58
+
59
+ async function load(opts: google.maps.MapOptions) {
60
+ await waitComplete();
61
+ const element = getElement();
62
+
63
+ map.value = new google.maps.Map(element, {
64
+ zoom: defaultZoom.value,
65
+ disableDefaultUI: true,
66
+ ...opts,
67
+ });
68
+
69
+ marker.value = new google.maps.Marker({
70
+ map: map.value,
71
+ animation: google.maps.Animation.DROP,
72
+ position: opts?.center,
73
+ });
74
+
75
+ // Adding custom zoom control to the map
76
+ const zoomControlDiv = document.createElement('div');
77
+ zoomControl(zoomControlDiv, map.value);
78
+ map.value.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControlDiv);
79
+
80
+ // Adding custom location finder to the map
81
+ if (isGeolocationAllowed.value) {
82
+ const locationControlDiv = document.createElement('div');
83
+ addYourLocationButton(locationControlDiv, map.value);
84
+
85
+ map.value.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(locationControlDiv);
86
+ }
87
+
88
+ // Setup bounds_changed event to keep the marker in center of the map
89
+ map.value.addListener('bounds_changed', () => {
90
+ marker.value?.setPosition(map.value?.getCenter());
91
+ });
92
+
93
+ map.value.addListener('zoom_changed', async () => {
94
+ const center = map.value?.getCenter();
95
+ if (!center) return;
96
+ const newLocation = await getLocationInfo(center);
97
+ if (newLocation?.latitude && onPlaceChanged) onPlaceChanged(newLocation);
98
+ });
99
+
100
+ // Setup dragend event to save new location after move the map
101
+ map.value.addListener('dragend', async () => {
102
+ const center = map.value?.getCenter();
103
+ if (!center) return;
104
+ const newLocation = await getLocationInfo(center);
105
+ if (newLocation?.latitude && onPlaceChanged) onPlaceChanged(newLocation);
106
+ });
107
+ }
108
+
109
+ async function loadPreview(opts: google.maps.MapOptions) {
110
+ await waitComplete();
111
+ const element = getElement();
112
+
113
+ map.value = new google.maps.Map(element, {
114
+ zoom: defaultZoom.value,
115
+ disableDefaultUI: true,
116
+ disableDoubleClickZoom: true,
117
+ gestureHandling: 'none',
118
+ keyboardShortcuts: false,
119
+ clickableIcons: false,
120
+ ...opts,
121
+ });
122
+
123
+ marker.value = new google.maps.Marker({
124
+ map: map.value,
125
+ animation: google.maps.Animation.DROP,
126
+ position: opts?.center,
127
+ });
128
+
129
+ // Setup bounds_changed event to keep the marker in center of the map
130
+ map.value.addListener('bounds_changed', () => {
131
+ marker.value?.setPosition(map.value?.getCenter());
132
+ });
133
+ }
134
+
135
+ async function getLocation() {
136
+ const [location, zoom, geoLocation] = await getLocationRaw(defaultZoom.value, defaultLocation);
137
+ defaultZoom.value = zoom;
138
+ isGeolocationAllowed.value = geoLocation;
139
+ return location;
140
+ }
141
+
142
+ function placeChanged(place: ILocationInfo) {
143
+ if (place?.latitude && place?.longitude) {
144
+ map.value?.panTo(new google.maps.LatLng(place.latitude, place.longitude));
145
+ map.value?.setZoom(16);
146
+ }
147
+ }
148
+
149
+ return {
150
+ map,
151
+ marker,
152
+ load,
153
+ loadPreview,
154
+ getLocation,
155
+ placeChanged,
156
+ };
157
+ }
@@ -0,0 +1 @@
1
+ export const CLAIMS_WIDGET_ID = 'axitech-claims-widget';
package/src/index.ts ADDED
@@ -0,0 +1,76 @@
1
+ import { App, defineCustomElement } from 'vue';
2
+ import * as components from './components';
3
+ import utils from './utils';
4
+ import { config, applyConfig as applyAdminConfig } from './utils/config';
5
+ import { Theme } from './utils/themeUtils';
6
+ import { ApiPaths, request } from './utils/request';
7
+
8
+ const WidgetCustomElement = defineCustomElement(components.ClaimsWidget);
9
+ customElements.define('claims-widget', WidgetCustomElement);
10
+
11
+ const WidgetTypes = {
12
+ claim: 'claim',
13
+ } as const;
14
+
15
+ export type WidgetType = keyof typeof WidgetTypes;
16
+
17
+ export type IWidgetOptions = {
18
+ clientId: string;
19
+ type?: WidgetType;
20
+ user: {
21
+ id: string;
22
+ name: string;
23
+ email: string;
24
+ phone: string;
25
+ };
26
+ insurance: {
27
+ provider: string;
28
+ policyNumber: string;
29
+ };
30
+ theme?: Theme;
31
+ };
32
+
33
+ /** Load widget as vue component to have more control over it */
34
+ export function install(app: App, options: IWidgetOptions & { apiKey: string }) {
35
+ load(options.apiKey, options);
36
+ for (const key in components) {
37
+ app.component(key, components[key as keyof typeof components]);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * load will use the apikey and load the client configuration any options you provide here will overwrite the config provided on the admin interface
43
+ * @param options
44
+ */
45
+ export async function load(apiKey: string, options: IWidgetOptions) {
46
+ try {
47
+ if (!options) {
48
+ throw new Error('You must pass the required options (user, insurance) to the widget');
49
+ }
50
+ config.value.isConfigLoading = true;
51
+ if (!apiKey) {
52
+ throw new Error('You must provide an API key');
53
+ }
54
+ config.value.apiKey = apiKey;
55
+ config.value.clientId = options.clientId;
56
+ const [result, error] = await request.get(ApiPaths.config);
57
+ if (error?.message) {
58
+ throw error.message;
59
+ }
60
+ if (!result) {
61
+ throw 'Unknown error happend, please try again later';
62
+ }
63
+ applyAdminConfig({ ...result, ...options });
64
+ } catch (error) {
65
+ config.value.error = error as string;
66
+ config.value.apiKey = '';
67
+ } finally {
68
+ config.value.isConfigLoading = false;
69
+ }
70
+ }
71
+
72
+ /** Constants of the widget */
73
+ export * from './constants';
74
+
75
+ /** Use applyTheme to apply theme changes at runtime if you have not had the chance to change them at initialisation */
76
+ export const applyConfig = utils.applyPublicConfig;
@@ -0,0 +1,18 @@
1
+ import { initializeApp } from 'firebase/app';
2
+ import { getStorage } from 'firebase/storage';
3
+
4
+ const firebaseConfig = {
5
+ apiKey: process.env.VITE_APP_API_KEY,
6
+ authDomain: process.env.VITE_APP_AUTH_DOMAIN,
7
+ projectId: process.env.VITE_APP_PROJECT_ID,
8
+ storageBucket: process.env.VITE_APP_STORAGE_BUCKET,
9
+ messagingSenderId: process.env.VITE_APP_MESSAGING_SENDER_ID,
10
+ appId: process.env.VITE_APP_APP_ID,
11
+ measurementId: process.env.VITE_APP_MEASUREMENT_ID,
12
+ };
13
+
14
+ // Initialize Firebase
15
+ export const app = initializeApp(firebaseConfig);
16
+
17
+ // Initialize Cloud Storage and get a reference to the service
18
+ export const storage = getStorage(app);
@@ -0,0 +1,88 @@
1
+ import { BasePlugin, PluginOptions } from '@uppy/core';
2
+ import { storage } from './firebase';
3
+ import { ref, uploadBytesResumable, getDownloadURL, UploadMetadata } from 'firebase/storage';
4
+ import Uppy from '@uppy/core';
5
+
6
+ interface StorageOptions extends PluginOptions {
7
+ basePath?: string;
8
+ }
9
+
10
+ export default class FirebaseCloudStorage extends BasePlugin<StorageOptions> {
11
+ public title = 'Firebase Cloud Storage';
12
+ public type = 'uploader';
13
+ public id = 'FirebaseCloudStorage';
14
+ public basePath = '';
15
+ constructor(uppy: Uppy, opts?: StorageOptions) {
16
+ super(uppy, opts);
17
+ this.basePath = opts?.basePath || '';
18
+ this.uploadFiles = this.uploadFiles.bind(this);
19
+ }
20
+
21
+ async uploadFiles(fileIds: string[]) {
22
+ const files = fileIds.map((file) => this.uppy.getFile(file));
23
+ this.uppy.emit('upload-start', files);
24
+ await Promise.all(
25
+ files.map((file) => {
26
+ return new Promise<void>((resolve, reject) => {
27
+ const fileRef = ref(
28
+ storage,
29
+ `${this.basePath.endsWith('/') ? this.basePath : `${this.basePath}/`}${file.name}`
30
+ );
31
+ const metaData: UploadMetadata = {
32
+ contentType: file.type,
33
+ };
34
+ const uploadTask = uploadBytesResumable(fileRef, file.data, metaData);
35
+
36
+ uploadTask.on(
37
+ 'state_changed',
38
+ (snapshot) => {
39
+ const progressInfo = {
40
+ uploader: this,
41
+ bytesUploaded: snapshot.bytesTransferred,
42
+ bytesTotal: snapshot.totalBytes,
43
+ };
44
+ this.uppy.emit('upload-progress', file, progressInfo);
45
+ },
46
+ (error) => {
47
+ let err = '';
48
+ switch (error.code) {
49
+ case 'storage/unauthorized':
50
+ err = 'Unauthorized';
51
+ break;
52
+ case 'storage/canceled':
53
+ err = 'canceled';
54
+ break;
55
+ case 'storage/unknown':
56
+ err = error.serverResponse || 'Unknown error';
57
+ break;
58
+ }
59
+ this.uppy.emit('upload-error', file, err);
60
+ reject(err);
61
+ },
62
+ () => {
63
+ getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
64
+ file.downloadUrl = downloadURL;
65
+ const progressInfo = {
66
+ uploader: this,
67
+ bytesUploaded: file.size,
68
+ bytesTotal: file.size,
69
+ };
70
+ this.uppy.emit('upload-progress', file, progressInfo);
71
+ this.uppy.emit('upload-success', file, uploadTask.snapshot, downloadURL);
72
+ resolve();
73
+ });
74
+ }
75
+ );
76
+ });
77
+ })
78
+ );
79
+ }
80
+
81
+ install() {
82
+ this.uppy.addUploader(this.uploadFiles);
83
+ }
84
+
85
+ uninstall() {
86
+ this.uppy.removeUploader(this.uploadFiles);
87
+ }
88
+ }
@@ -0,0 +1,57 @@
1
+ import { Theme } from './themeUtils';
2
+ import MapMarker from '../components/claims-widget/assets/marker.svg';
3
+
4
+ export async function loadAutocompleteStyle(theme: Theme) {
5
+ const styleElem = document.createElement('style', {
6
+ is: 'claims-widget-style',
7
+ });
8
+ const svgContent = await (await fetch(MapMarker)).text();
9
+ let primaryMapMarker = document.createElement('svg') as unknown as SVGElement;
10
+ primaryMapMarker.innerHTML = svgContent;
11
+ primaryMapMarker = primaryMapMarker.firstChild as SVGElement;
12
+ primaryMapMarker.style.fill = theme.primary;
13
+ styleElem.innerHTML = `
14
+ .pac-item {
15
+ padding: 8px 16px;
16
+ cursor: pointer;
17
+ background-color: #fff;
18
+ border-radius: 2px;
19
+ font-family: inherit;
20
+ }
21
+
22
+ .pac-icon {
23
+ background-image: url(${MapMarker}) !important;
24
+ background-repeat: no-repeat;
25
+ background-position: center;
26
+ background-size: 16px;
27
+ margin: 4px 6px 6px 0;
28
+ vertical-align: middle;
29
+ }
30
+
31
+ .pac-item-selected, .pac-item:hover {
32
+ color: ${theme.primary};
33
+ }
34
+
35
+ .pac-item-selected .pac-icon {
36
+ background-image: url("data:image/svg+xml,${encodeURIComponent(primaryMapMarker.outerHTML)}") !important;;
37
+ background-repeat: no-repeat;
38
+ background-position: center;
39
+ background-size: 16px;
40
+ margin: 4px 6px 6px 0;
41
+ vertical-align: middle;
42
+ }
43
+
44
+ .pac-item:hover .pac-icon {
45
+ background-image: url("data:image/svg+xml,${encodeURIComponent(primaryMapMarker.outerHTML)}") !important;;
46
+ }
47
+
48
+ .pac-item-selected .pac-item-query {
49
+ color: ${theme.primary};
50
+ }
51
+
52
+ .pac-item:hover .pac-item-query {
53
+ color: ${theme.primary};
54
+ }
55
+ `;
56
+ document.head.append(styleElem);
57
+ }
@@ -0,0 +1,96 @@
1
+ interface Palette {
2
+ [key: number]: string;
3
+ }
4
+ interface Rgb {
5
+ r: number;
6
+ g: number;
7
+ b: number;
8
+ }
9
+
10
+ export const defaultShades = {
11
+ '50': '#F3F6FC',
12
+ '100': '#E6EEF8',
13
+ '200': '#C8DBEF',
14
+ '300': '#97BCE2',
15
+ '400': '#5F98D1',
16
+ '500': '#3B7BBC',
17
+ '600': '#2A609D',
18
+ '700': '#234E81',
19
+ '800': '#21436B',
20
+ '900': '#203A5A',
21
+ '950': '#15253C',
22
+ } as Palette;
23
+
24
+ function hexToRgb(hex: string): Rgb | null {
25
+ const sanitizedHex = hex.replaceAll('##', '#');
26
+ const colorParts = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(sanitizedHex);
27
+ if (!colorParts) {
28
+ return null;
29
+ }
30
+ const [, r, g, b] = colorParts;
31
+ return {
32
+ r: parseInt(r, 16),
33
+ g: parseInt(g, 16),
34
+ b: parseInt(b, 16),
35
+ } as Rgb;
36
+ }
37
+ function rgbToHex(r: number, g: number, b: number): string {
38
+ const toHex = (c: number) => `0${c.toString(16)}`.slice(-2);
39
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
40
+ }
41
+ function lighten(hex: string, intensity: number): string {
42
+ const color = hexToRgb(`#${hex}`);
43
+ if (!color) {
44
+ return '';
45
+ }
46
+ const r = Math.round(color.r + (255 - color.r) * intensity);
47
+ const g = Math.round(color.g + (255 - color.g) * intensity);
48
+ const b = Math.round(color.b + (255 - color.b) * intensity);
49
+ return rgbToHex(r, g, b);
50
+ }
51
+ function darken(hex: string, intensity: number): string {
52
+ const color = hexToRgb(hex);
53
+ if (!color) {
54
+ return '';
55
+ }
56
+ const r = Math.round(color.r * intensity);
57
+ const g = Math.round(color.g * intensity);
58
+ const b = Math.round(color.b * intensity);
59
+ return rgbToHex(r, g, b);
60
+ }
61
+ export function generateShades(baseColor: string): Palette {
62
+ const response: Palette = {
63
+ 500: `#${baseColor}`.replace('##', '#'),
64
+ };
65
+ const intensityMap: {
66
+ [key: number]: number;
67
+ } = {
68
+ 50: 0.95,
69
+ 100: 0.9,
70
+ 200: 0.75,
71
+ 300: 0.6,
72
+ 400: 0.3,
73
+ 600: 0.9,
74
+ 700: 0.75,
75
+ 800: 0.6,
76
+ 900: 0.49,
77
+ };
78
+ [50, 100, 200, 300, 400].forEach((level) => {
79
+ response[level] = lighten(baseColor, intensityMap[level]);
80
+ });
81
+ [600, 700, 800, 900].forEach((level) => {
82
+ response[level] = darken(baseColor, intensityMap[level]);
83
+ });
84
+ return response as Palette;
85
+ }
86
+ export function setColorShades(cssProperty = '--primary-color', color?: string) {
87
+ const root = document.documentElement;
88
+ const shades = color ? generateShades(color) : defaultShades;
89
+ if (root?.style) {
90
+ Object.keys(shades)
91
+ .map(Number)
92
+ .forEach((shade) => {
93
+ root.style.setProperty(`${cssProperty}-${shade}`, shades[shade]);
94
+ });
95
+ }
96
+ }
@@ -0,0 +1,140 @@
1
+ import { IApiPath } from './request';
2
+ import { ref } from 'vue';
3
+ import type { InjectionKey } from 'vue';
4
+ import { Theme } from './themeUtils';
5
+ import { ILocationInfo } from './map';
6
+ const rootKey = Symbol('root_element') as InjectionKey<string>;
7
+
8
+ export const getBaseUrl = (path: IApiPath) => `${process.env.VITE_APP_API_BASE_URL}${config.value.clientId}/${path}`;
9
+
10
+ export type WidgetConfig = {
11
+ user: UserConfig;
12
+ insurance: InsuranceConfig;
13
+ theme: Theme;
14
+ displayName: string;
15
+ contact: ContactConfig;
16
+ };
17
+
18
+ export type UserConfig = {
19
+ id: string;
20
+ name: string;
21
+ email: string;
22
+ phone: string;
23
+ };
24
+
25
+ export type ContactConfig = {
26
+ phone: string;
27
+ email: string;
28
+ };
29
+
30
+ export type InsuranceConfig = {
31
+ provider: string;
32
+ policyNumber: string;
33
+ };
34
+
35
+ export type CollisionSubmission = {
36
+ notificationTime: string;
37
+ date: string;
38
+ location: ILocationInfo | undefined;
39
+ vehicleImages: string[];
40
+ additionalImages: string[];
41
+ note: string;
42
+ vehicleRoadWorthy: boolean;
43
+ driver: boolean;
44
+ injured: boolean;
45
+ thirdPartiesInvolved: boolean;
46
+ witnessesPresent: boolean;
47
+ user: {
48
+ id: string;
49
+ name: string;
50
+ email: string;
51
+ phone: string;
52
+ };
53
+ insurance: {
54
+ provider: string;
55
+ policyNumber: string;
56
+ };
57
+ incidentType: string;
58
+ };
59
+
60
+ const defaultConfig = {
61
+ clientId: '',
62
+ user: {
63
+ id: 'Unknown',
64
+ name: '',
65
+ email: '',
66
+ phone: '',
67
+ } satisfies UserConfig,
68
+ apiKey: '',
69
+ insurance: {
70
+ provider: '',
71
+ policyNumber: '',
72
+ } satisfies InsuranceConfig,
73
+ theme: {
74
+ primary: '#2A609D',
75
+ secondary: '#004FBC',
76
+ positive: '#408825',
77
+ negative: '#DE0808',
78
+ fontPrimary: '#000000',
79
+ fontSecondary: '#dddddd',
80
+ },
81
+ contact: {
82
+ phone: '',
83
+ email: '',
84
+ } satisfies ContactConfig,
85
+ isConfigLoading: true,
86
+ error: '',
87
+ name: '',
88
+ allowedDomains: [] as string[],
89
+ correspondenceEmail: [] as string[],
90
+ rootKey,
91
+ id: '',
92
+ onSubmit: (() => null) as (type: '', body: CollisionSubmission) => void,
93
+ };
94
+
95
+ export const config = ref({} as typeof defaultConfig);
96
+
97
+ const callbacks = new Set<(config: typeof defaultConfig) => void>();
98
+ export function onConfigChange(callback: (config: typeof defaultConfig) => void) {
99
+ callbacks.add(callback);
100
+ return () => {
101
+ callbacks.delete(callback);
102
+ };
103
+ }
104
+
105
+ export function applyConfig(newConfig: Partial<typeof defaultConfig>) {
106
+ // do not allow overwrite of the apiKey
107
+ const apiKey = config.value.apiKey;
108
+ Object.assign(config.value, newConfig);
109
+ config.value.apiKey = apiKey;
110
+ callbacks.forEach((callback) => callback(config.value));
111
+ }
112
+
113
+ type RecursivePartial<T> = {
114
+ [P in keyof T]?: T[P] extends (infer U)[]
115
+ ? RecursivePartial<U>[]
116
+ : T[P] extends object | undefined
117
+ ? RecursivePartial<T[P]>
118
+ : T[P];
119
+ };
120
+
121
+ export function applyPublicConfig(newConfig: RecursivePartial<WidgetConfig>) {
122
+ if (newConfig.user) {
123
+ config.value.user = { ...config.value.user, ...newConfig.user };
124
+ }
125
+ if (newConfig.insurance) {
126
+ config.value.insurance = { ...config.value.insurance, ...newConfig.insurance };
127
+ }
128
+ if (newConfig.theme) {
129
+ config.value.theme = { ...config.value.theme, ...newConfig.theme };
130
+ }
131
+ if (newConfig.theme) {
132
+ config.value.theme = { ...config.value.theme, ...newConfig.theme };
133
+ }
134
+ if (newConfig.displayName) {
135
+ config.value.name = newConfig.displayName;
136
+ }
137
+ callbacks.forEach((callback) => callback(config.value));
138
+ }
139
+
140
+ applyConfig(defaultConfig);
@@ -0,0 +1,40 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ // @ts-nocheck
3
+ import { defineCustomElement as VueDefineCustomElement, h, createApp, getCurrentInstance } from 'vue';
4
+
5
+ const nearestElement = (el) => {
6
+ while (el?.nodeType !== 1 /* ELEMENT */) el = el.parentElement;
7
+ return el;
8
+ };
9
+
10
+ export const defineCustomElement = (component) =>
11
+ VueDefineCustomElement({
12
+ setup() {
13
+ const app = createApp();
14
+ app.mixin({
15
+ mounted() {
16
+ const insertStyles = (styles) => {
17
+ if (styles?.length) {
18
+ this.__style = document.createElement('style');
19
+ this.__style.innerText = styles.join().replace(/\n/g, '');
20
+ nearestElement(this.$el).prepend(this.__style);
21
+ }
22
+ };
23
+ insertStyles(this.$?.type.styles);
24
+ if (this.$options.components) {
25
+ for (const comp of Object.values(this.$options.components)) {
26
+ insertStyles(comp.styles);
27
+ }
28
+ }
29
+ },
30
+ unmounted() {
31
+ this.__style?.remove();
32
+ },
33
+ });
34
+
35
+ const inst = getCurrentInstance();
36
+ Object.assign(inst.appContext, app._context);
37
+
38
+ return () => h(component);
39
+ },
40
+ });
@@ -0,0 +1,24 @@
1
+ import Font from '../assets/fonts/Volvo Novum-Regular.woff';
2
+ import FontMedium from '../assets/fonts/Volvo Novum-Medium.woff';
3
+
4
+ export function loadFonts() {
5
+ const styleElem = document.createElement('style', {
6
+ is: 'claims-widget-style',
7
+ });
8
+ styleElem.innerHTML = `
9
+ @font-face {
10
+ font-family: "WidgetFont";
11
+ font-style: normal;
12
+ font-display: swap;
13
+ src: url('${Font}') format('woff');
14
+ }
15
+ @font-face {
16
+ font-family: "WidgetFont";
17
+ src: url('${FontMedium}') format("woff");
18
+ font-weight: bold;
19
+ font-display: swap;
20
+ font-style: normal;
21
+ }
22
+ `;
23
+ document.head.append(styleElem);
24
+ }