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,77 @@
1
+ <template>
2
+ <div :class="[bgClass, loaderClass, 'relative overflow-hidden']">
3
+ <div class="shimmer absolute top-0 right-0 bottom-0 left-0" :style="shimmerStyle"></div>
4
+ <slot />
5
+ </div>
6
+ </template>
7
+
8
+ <script lang="ts">
9
+ const LOADER_TYPES = { rectangle: 'rectangle', circle: 'circle' };
10
+
11
+ const LOADER_CSS_CLASSES = {
12
+ [LOADER_TYPES.rectangle]: 'rounded',
13
+ [LOADER_TYPES.circle]: 'rounded-full',
14
+ };
15
+
16
+ type LoaderTypesKeys = keyof typeof LOADER_TYPES;
17
+ type LoaderTypesValues = (typeof LOADER_TYPES)[LoaderTypesKeys];
18
+
19
+ const SHIMMER_COLOR = '#ffffff';
20
+
21
+ const isHexColor = (hexColor: string) => {
22
+ const hex = hexColor.replace('#', '');
23
+
24
+ return typeof hexColor === 'string' && hexColor.startsWith('#') && hex.length === 6 && !isNaN(Number('0x' + hex));
25
+ };
26
+
27
+ const hexToRgb = (hex: string) => `${hex.match(/\w\w/g)?.map((x) => +`0x${x}`)}`;
28
+ </script>
29
+
30
+ <script setup lang="ts">
31
+ import { computed } from 'vue';
32
+
33
+ const props = defineProps({
34
+ type: {
35
+ type: String,
36
+ default: LOADER_TYPES.rectangle,
37
+ validator(value: LoaderTypesValues) {
38
+ return Object.values(LOADER_TYPES).includes(value);
39
+ },
40
+ },
41
+ bgClass: {
42
+ type: String,
43
+ default: 'bg-gray-300',
44
+ },
45
+ cssClass: {
46
+ type: String,
47
+ default: '',
48
+ },
49
+ shimmerColor: {
50
+ type: String,
51
+ default: SHIMMER_COLOR,
52
+ },
53
+ });
54
+
55
+ const shimmerStyle = computed(() => {
56
+ const rgb = isHexColor(props.shimmerColor) ? hexToRgb(props.shimmerColor) : SHIMMER_COLOR;
57
+
58
+ return {
59
+ backgroundImage: `linear-gradient(90deg, rgba(${rgb}, 0) 0%, rgba(${rgb}, 0.2) 20%, rgba(${rgb}, 0.5) 60%, rgba(${rgb}, 0))`,
60
+ };
61
+ });
62
+
63
+ const loaderClass = computed(() => (props.cssClass ? props.cssClass : LOADER_CSS_CLASSES[props.type]));
64
+ </script>
65
+
66
+ <style lang="css" scoped>
67
+ .shimmer {
68
+ transform: translateX(-100%);
69
+ animation: shimmer 1.4s infinite;
70
+ }
71
+
72
+ @keyframes shimmer {
73
+ 100% {
74
+ transform: translateX(100%);
75
+ }
76
+ }
77
+ </style>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <section id="widget-confirm" class="w-full mx-auto flex flex-col justify-start items-center content-center">
3
+ <p class="title3-bold mb-10 w-full text-start">Details Submitted Successfully</p>
4
+ <div class="flex flex-col items-center">
5
+ <SubmittedSuccessfully></SubmittedSuccessfully>
6
+ <div class="py-5 w-full flex flex-col items-center text-center gap-4">
7
+ <div class="flex flex-col items-center">
8
+ <h2 class="title3-bold font-medium mb-4">We'll be in touch</h2>
9
+ <p class="body1 text-fontSecondary-500 mb-9">
10
+ Thank you for letting us know about the collision, we'll review these details and contact you soon
11
+ </p>
12
+ </div>
13
+ <div class="w-full flex flex-col items-center">
14
+ <div
15
+ class="flex items-start flex-nowrap justify-start gap-2"
16
+ v-for="(item, index) in progressList"
17
+ :key="index"
18
+ >
19
+ <div class="flex flex-col items-center relative h-full justify-between">
20
+ <Icon
21
+ :icon="item.checked ? 'mdi:check-circle' : 'mdi:circle-outline'"
22
+ width="29px"
23
+ class="text-primary-500"
24
+ ></Icon>
25
+ <div v-if="index !== progressList.length - 1" class="checklist-line"></div>
26
+ </div>
27
+ <span class="text-start body1 max-w-sm">{{ item.text }}</span>
28
+ </div>
29
+ </div>
30
+ <div class="border w-full rounded-md">
31
+ <a :href="`tel:${config.contact.phone}`" class="w-full hover:bg-slate-100 cursor-pointer p-4 block">
32
+ <div class="body1 flex justify-between h-full items-center">
33
+ <div class="text-start">
34
+ <p>Talk to a person</p>
35
+ <span class="body2 text-fontSecondary-500">{{ config.contact.phone }}</span>
36
+ </div>
37
+ <Icon icon="mdi:chevron-right" height="24px"></Icon>
38
+ </div>
39
+ </a>
40
+ <a :href="`mailto:${config.contact.email}`" class="w-full cursor-pointer hover:bg-slate-100 p-4 block">
41
+ <div class="body1 flex justify-between h-full items-center">
42
+ <div class="text-start">
43
+ <p>Email us</p>
44
+ <span class="body2 text-fontSecondary-500">{{ config.contact.email }}</span>
45
+ </div>
46
+ <Icon icon="mdi:chevron-right" height="24px"></Icon>
47
+ </div>
48
+ </a>
49
+ </div>
50
+ <a
51
+ class="action1-bold text-primary hover:text-primary-400 underline underline-offset-2 cursor-pointer"
52
+ @click.prevent="emit('back')"
53
+ >
54
+ Report another incident
55
+ </a>
56
+ </div>
57
+ </div>
58
+ </section>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { Icon } from '@iconify/vue';
63
+ import SubmittedSuccessfully from './illustration/SubmittedSuccessfully.vue';
64
+ import { Ref, inject, onMounted } from 'vue';
65
+ import { config } from 'src/utils/config';
66
+
67
+ const emit = defineEmits<{
68
+ (event: 'back'): void;
69
+ }>();
70
+
71
+ const rootElement = inject<Ref<HTMLDivElement>>(config.value.rootKey);
72
+
73
+ const progressList = [
74
+ { text: 'We can now review the information to determine the next steps', checked: true },
75
+ { text: 'We`ve started to generate options for where to recover your vehicle', checked: true },
76
+ { text: 'Our claims experts will follow up with some detailed questions', checked: false },
77
+ ];
78
+
79
+ onMounted(() => {
80
+ if (!rootElement?.value) return;
81
+ rootElement.value.scrollIntoView({ behavior: 'smooth', block: 'start' });
82
+ });
83
+ </script>
84
+
85
+ <style scoped></style>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <div>
3
+ <h2 class="title3-bold font-medium mb-4">How to make a claim</h2>
4
+ <p class="body3 text-fontSecondary-500 mb-9">Please select the type of claim you are looking for support with</p>
5
+ <ul role="list" class="flex flex-col gap-y-4">
6
+ <li v-for="card in cards" :key="card.name" class="flex">
7
+ <button class="flex items-start rounded p-4 border border-grey-400 w-full" @click="card.onSelect">
8
+ <div class="flex h-12 w-12 items-center justify-center overflow-hidden">
9
+ <Icon :icon="card.icon" :alt="`${card.name}-icon`" class="h-24 w-24 text-primary" />
10
+ </div>
11
+
12
+ <div class="ml-4">
13
+ <h2 class="body1-bold text-start">{{ card.name }}</h2>
14
+ <p class="body3 text-start text-fontSecondary-500">{{ card.description }}</p>
15
+ </div>
16
+ </button>
17
+ </li>
18
+ </ul>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { ref } from 'vue';
24
+ import { Icon } from '@iconify/vue';
25
+
26
+ const emit = defineEmits<{
27
+ (event: 'select', index: number): void;
28
+ }>();
29
+
30
+ const cards = ref([
31
+ {
32
+ name: 'Accident',
33
+ description: 'Start a Claim for an Accident online',
34
+ icon: 'uil:car',
35
+ onSelect: () => {
36
+ emit('select', 0);
37
+ },
38
+ },
39
+ {
40
+ name: 'Windscreen',
41
+ description: 'Claim for Windscreen repair',
42
+ icon: 'mdi:car-windshield-outline',
43
+ onSelect: () => {
44
+ emit('select', 1);
45
+ },
46
+ },
47
+ {
48
+ name: 'Fire, Water, Theft or Vandalism',
49
+ description: 'Claim for another type of incident',
50
+ icon: 'mdi:car-door',
51
+ onSelect: () => {
52
+ emit('select', 2);
53
+ },
54
+ },
55
+ ]);
56
+ </script>
57
+
58
+ <style scoped></style>
@@ -0,0 +1,267 @@
1
+ <template>
2
+ <main
3
+ class="randomclass w-full h-full mx-auto flex flex-col justify-start items-center content-center"
4
+ id="claims-accident"
5
+ >
6
+ <button :disabled="isLoading" class="w-full flex flex-row mb-10 items-center" @click="emit('back')">
7
+ <Icon icon="mdi-light:chevron-left" height="32" />
8
+ <p class="title3-bold">Accident Support</p>
9
+ </button>
10
+ <section class="flex flex-col items-center w-full mt-6">
11
+ <Collapse></Collapse>
12
+ </section>
13
+ <section class="flex flex-col items-center w-full gap-6 mb-8 mt-6">
14
+ <Accident />
15
+ <h5 class="title4-bold">If you've been in an accident</h5>
16
+ <p class="body1 text-fontSecondary-500">
17
+ Let us know as soon as possible, so we can get you moving again. We've built a helpful tool to make this quick
18
+ and easy to do online
19
+ </p>
20
+ </section>
21
+ <SectionWrapper id="date-picker" class="gap-6" title="Approximately when did the collision occur?">
22
+ <DatePicker v-model="date" :disabled="isLoading"></DatePicker>
23
+ </SectionWrapper>
24
+ <MapSection
25
+ v-if="mapKey"
26
+ v-model="location"
27
+ id="location-selector"
28
+ class="gap-6"
29
+ title="Where did the collision occur?"
30
+ ></MapSection>
31
+ <SectionWrapper v-for="value in selectValues" :key="value.key" :id="value.key" class="gap-6">
32
+ <template #header>
33
+ <div class="flex w-full justify-between items-center">
34
+ <h3 class="title4-bold">{{ value.title }}</h3>
35
+ <Icon icon="mdi:info-outline" height="20"></Icon>
36
+ </div>
37
+ </template>
38
+ <SelectButton
39
+ v-model="value.value"
40
+ :disabled="isLoading"
41
+ :options="value.options"
42
+ :button-class="value.customClass"
43
+ ></SelectButton>
44
+ </SectionWrapper>
45
+ <ImageSection
46
+ v-model="vehicleImages"
47
+ id="your-vehicle"
48
+ :disabled="isLoading"
49
+ title="Your Vehicle"
50
+ card-title="Add photos of your vehicle"
51
+ card-content="Adding photos and details of the damage to your vehicle helps us to assess it earlier and find the right services for you, ultimately resulting in a more efficient repair"
52
+ icon="mdi:car-door"
53
+ ></ImageSection>
54
+ <ImageSection
55
+ v-model="additionalImages"
56
+ id="additional-photos"
57
+ :disabled="isLoading"
58
+ title="Additional Photos"
59
+ card-title="Add additional photos and details"
60
+ card-content="To protect your position, we recommend that you try to collect photos of the scene, damaged property and any additional details that help to 'tell the story' of what happened"
61
+ icon="mdi:image-outline"
62
+ ></ImageSection>
63
+ <NoteSection v-model="note" :disabled="isLoading"></NoteSection>
64
+ <SectionWrapper id="contact-time">
65
+ <div>
66
+ <label class="title4-bold">When's the best time to call you?</label>
67
+ <p class="body1 text-fontSecondary500">We'll try to call you at your preferred time</p>
68
+ <fieldset class="mt-5">
69
+ <legend class="sr-only">Notification method</legend>
70
+ <div class="flex flex-col gap-5">
71
+ <div v-for="notificationTime in notificationTimes" :key="notificationTime.id" class="flex items-center">
72
+ <input
73
+ :disabled="isLoading"
74
+ :id="notificationTime.id"
75
+ name="notification-method"
76
+ type="radio"
77
+ :checked="notificationTime.id === selectedNotificationTime"
78
+ class="h-5 w-5 border-primary-500 border-2 text-primary-600 focus:ring-primary-600 focus-within:bg-primary-500 active:bg-primary-500"
79
+ :class="{ '!bg-primary-500': notificationTime.id === selectedNotificationTime }"
80
+ @input="selectedNotificationTime = notificationTime.id"
81
+ />
82
+ <label :for="notificationTime.id" class="ml-3 block body1-bold">{{ notificationTime.title }}</label>
83
+ </div>
84
+ </div>
85
+ </fieldset>
86
+ </div>
87
+ </SectionWrapper>
88
+ <SectionWrapper id="almost-there">
89
+ <template #header>
90
+ <h3 class="title2-bold">Almost there...</h3>
91
+ </template>
92
+ <fieldset>
93
+ <legend class="sr-only">I Accept Privacy Policy</legend>
94
+ <div class="space-y-5">
95
+ <div class="relative flex items-start">
96
+ <div class="flex h-6 items-center">
97
+ <input
98
+ v-model="isPrivacyAccepted"
99
+ id="comments"
100
+ :disabled="isLoading"
101
+ name="comments"
102
+ type="checkbox"
103
+ :class="{ '!bg-primary-500': isPrivacyAccepted }"
104
+ class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-600"
105
+ />
106
+ </div>
107
+ <div class="ml-3 text-sm leading-6">
108
+ <label for="comments" class="body1 text-fontSecondary500">
109
+ By submitting the details above, I agree to be contacted by the Volvo claims team via email or telephone
110
+ regarding my claim. For more information please see our
111
+ <span class="">Privacy Policy</span>
112
+ </label>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </fieldset>
117
+ </SectionWrapper>
118
+ <div id="almost-there" class="px-8 w-full">
119
+ <Button
120
+ class="bg-primary text-white w-full flex justify-center"
121
+ :loading="isLoading"
122
+ :disabled="isLoading"
123
+ @click="submitResult"
124
+ >
125
+ <div class="flex flex-nowrap gap-2 justify-center items-center w-full">
126
+ <span class="title2-bold">Continue</span>
127
+ <Icon icon="mdi:chevron-right" height="28px"></Icon>
128
+ </div>
129
+ </Button>
130
+ <div
131
+ v-if="errorMessage"
132
+ class="mt-4 w-full border-2 border-negative-500 bg-negative-100 text-negative-500 font-bold rounded p-4"
133
+ >
134
+ {{ errorMessage }}
135
+ </div>
136
+ </div>
137
+ </main>
138
+ </template>
139
+
140
+ <script setup lang="ts">
141
+ import Button from 'components/base/Button.vue';
142
+ import Collapse from 'components/base/Collapse.vue';
143
+ import DatePicker from 'components/base/DatePicker.vue';
144
+ import MapSection from './MapSection.vue';
145
+ import SelectButton from 'components/base/SelectButton.vue';
146
+ import Accident from '../../components/illustration/Accident.vue';
147
+ import SectionWrapper from 'components/claims-widget/components/accident/SectionWrapper.vue';
148
+ import ImageSection from 'components/claims-widget/components/accident/ImageSection.vue';
149
+ import NoteSection from 'components/claims-widget/components/accident/NoteSection.vue';
150
+
151
+ import { Icon } from '@iconify/vue';
152
+ import { ref } from 'vue';
153
+ import { ILocationInfo } from 'src/utils/map';
154
+ import { config } from 'src/utils/config';
155
+ import { ApiPaths, request } from 'src/utils/request';
156
+
157
+ const emit = defineEmits<{
158
+ (event: 'back'): void;
159
+ (event: 'next'): void;
160
+ }>();
161
+ const mapKey = process.env.VITE_APP_GMAPS_KEY;
162
+ const errorMessage = ref('');
163
+ const isLoading = ref(false);
164
+ const isPrivacyAccepted = ref(false);
165
+
166
+ const date = ref('');
167
+ const note = ref('');
168
+ const location = ref<ILocationInfo>();
169
+ const selectedNotificationTime = ref('morning');
170
+ const additionalImages = ref<string[]>([]);
171
+ const vehicleImages = ref<string[]>([]);
172
+
173
+ const selectValues = ref([
174
+ {
175
+ options: [
176
+ { label: 'My Vehicle Is Safe To Drive', value: true },
177
+ { label: 'No, I Will need Recovery', value: false },
178
+ ],
179
+ value: false,
180
+ key: 'vehicleRoadWorthy',
181
+ customClass: 'w-full col-span-full sm:col-auto',
182
+ title: 'Is your vehicle roadworthy?',
183
+ },
184
+ {
185
+ options: [
186
+ { label: 'Yes', value: true },
187
+ { label: 'No', value: false },
188
+ ],
189
+ value: false,
190
+ key: 'driver',
191
+ title: 'Were you the driver?',
192
+ },
193
+ {
194
+ options: [
195
+ { label: 'Yes', value: true },
196
+ { label: 'No', value: false },
197
+ ],
198
+ value: false,
199
+ key: 'injured',
200
+ title: 'Were you injured?',
201
+ },
202
+ {
203
+ options: [
204
+ { label: 'Yes', value: true },
205
+ { label: 'No', value: false },
206
+ ],
207
+ value: false,
208
+ key: 'thirdPartiesInvolved',
209
+ title: 'Were any other third parties involved?',
210
+ },
211
+ {
212
+ options: [
213
+ { label: 'Yes', value: true },
214
+ { label: 'No', value: false },
215
+ ],
216
+ value: false,
217
+ key: 'witnessesPresent',
218
+ title: 'Were any witnesses present?',
219
+ },
220
+ ]);
221
+
222
+ const notificationTimes = [
223
+ { id: 'morning', title: 'Morning' },
224
+ { id: 'afternoon', title: 'Afternoon' },
225
+ { id: 'evening', title: 'Evening' },
226
+ { id: 'anytime', title: 'Call anytime' },
227
+ ];
228
+
229
+ async function submitResult() {
230
+ if (!isPrivacyAccepted.value) {
231
+ errorMessage.value = 'You must accept the privacy policy before proceeding!';
232
+ return;
233
+ }
234
+ if (!location.value?.formatted_address) {
235
+ errorMessage.value = 'Please provide provide your location!';
236
+ return;
237
+ }
238
+ errorMessage.value = '';
239
+ isLoading.value = true;
240
+
241
+ const result = {
242
+ notificationTime: selectedNotificationTime.value,
243
+ ...(Object.fromEntries(selectValues.value.map(({ value, key }) => [key, value])) as {
244
+ vehicleRoadWorthy: boolean;
245
+ driver: boolean;
246
+ injured: boolean;
247
+ thirdPartiesInvolved: boolean;
248
+ witnessesPresent: boolean;
249
+ }),
250
+ date: date.value,
251
+ location: location.value,
252
+ vehicleImages: vehicleImages.value,
253
+ additionalImages: additionalImages.value,
254
+ note: note.value,
255
+ user: config.value.user,
256
+ insurance: config.value.insurance,
257
+ incidentType: 'Collision',
258
+ };
259
+ isLoading.value = true;
260
+ const [, error] = await request.post(ApiPaths.submission, result);
261
+ if (error) {
262
+ errorMessage.value = error.message;
263
+ }
264
+ isLoading.value = false;
265
+ emit('next');
266
+ }
267
+ </script>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <SectionWrapper>
3
+ <template #header>
4
+ <div class="w-full flex justify-between items-center">
5
+ <h3 class="title4-bold">{{ title }}</h3>
6
+ <Button
7
+ v-if="previewImages?.length"
8
+ @click="openVehicleImageUpload"
9
+ :disabled="disabled"
10
+ class="!w-6 !h-6 !p-2 !rounded-full items-center justify-center flex"
11
+ >
12
+ <Icon icon="mdi:pencil"></Icon>
13
+ </Button>
14
+ </div>
15
+ </template>
16
+ <ActionCard
17
+ v-if="!previewImages?.length"
18
+ :title="cardTitle"
19
+ :icon="icon"
20
+ :content="cardContent"
21
+ :disabled="disabled"
22
+ action="Add photos"
23
+ @action="openVehicleImageUpload"
24
+ ></ActionCard>
25
+ <div
26
+ v-else
27
+ class="grid grid-cols-[repeat(auto-fill,_minmax(80px,_1fr))] sm:grid-cols-[repeat(auto-fill,_minmax(120px,_1fr))] gap-4 w-full"
28
+ >
29
+ <div class="w-full h-full" v-for="(image, index) in previewImages" :key="image">
30
+ <img
31
+ :src="image"
32
+ height="80"
33
+ :alt="`vehicle-damage${index}`"
34
+ class="rounded aspect-square object-cover w-full sm:h-28"
35
+ />
36
+ </div>
37
+ </div>
38
+ <ImageUpload
39
+ v-model="isImageUploadOpen"
40
+ :title="cardTitle"
41
+ :info="cardContent"
42
+ @imagesAdded="saveimages"
43
+ ></ImageUpload>
44
+ </SectionWrapper>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import Button from 'components/base/Button.vue';
49
+ import ActionCard from 'components/base/ActionCard.vue';
50
+ import ImageUpload from 'components/base/uploader/ImageUpload.vue';
51
+ import SectionWrapper from './SectionWrapper.vue';
52
+
53
+ import { Icon } from '@iconify/vue';
54
+ import { ref } from 'vue';
55
+ import { useVModel } from '@vueuse/core';
56
+
57
+ const props = defineProps<{
58
+ title: string;
59
+ cardTitle: string;
60
+ cardContent: string;
61
+ icon: string;
62
+ modelValue: string[];
63
+ disabled?: boolean;
64
+ }>();
65
+
66
+ const emit = defineEmits<{
67
+ (e: 'update:modelValue', images: string[]): void;
68
+ }>();
69
+
70
+ const isImageUploadOpen = ref(false);
71
+ const previewImages = useVModel(props, 'modelValue', emit);
72
+
73
+ function openVehicleImageUpload() {
74
+ isImageUploadOpen.value = true;
75
+ }
76
+ function saveimages(images: string[]) {
77
+ isImageUploadOpen.value = false;
78
+ previewImages.value = images;
79
+ }
80
+ </script>
81
+
82
+ <style scoped></style>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <section class="flex flex-col items-start w-full gap-3 mb-8">
3
+ <slot name="header">
4
+ <div class="w-full flex justify-between items-center">
5
+ <h3 class="title4-bold">{{ title }}</h3>
6
+ <div v-if="location?.latitude" class="flex items-center gap-2">
7
+ <Button @click="toggleMap(true)" class="!w-6 !h-6 !p-2 !rounded-full items-center justify-center flex">
8
+ <Icon icon="mdi:pencil"></Icon>
9
+ </Button>
10
+ <Button
11
+ @click="isOpenDeleteModal = true"
12
+ class="!w-6 !h-6 !p-2 !rounded-full items-center justify-center flex"
13
+ >
14
+ <Icon icon="mdi:trash-can-outline"></Icon>
15
+ </Button>
16
+ </div>
17
+ </div>
18
+ </slot>
19
+ <div class="flex flex-row flex-nowrap w-full justify-start gap-5">
20
+ <GoogleMap v-model="location" :is-open="isMapOpen" @update:is-open="toggleMap"></GoogleMap>
21
+ </div>
22
+ <Modal v-if="isOpenDeleteModal" v-model="isOpenDeleteModal">
23
+ <template #header><p class="font-bold">Are you sure you want to delete this location?</p> </template>
24
+ <div class="w-full h-full flex flex-col gap-4 items-center">
25
+ <p>This action cannot be undone</p>
26
+ </div>
27
+ <template #footer>
28
+ <div class="w-full flex justify-center">
29
+ <Button class="bg-primary text-white px-12" @click="deleteLocation">
30
+ <div class="flex flex-nowrap gap-2 justify-center items-center">
31
+ <span class="title2-bold">Delete</span>
32
+ </div>
33
+ </Button>
34
+ </div>
35
+ </template>
36
+ </Modal>
37
+ </section>
38
+ </template>
39
+
40
+ <script lang="ts" setup>
41
+ import { computed, defineAsyncComponent, ref } from 'vue';
42
+ import { ILocationInfo } from 'src/utils/map';
43
+ import { Icon } from '@iconify/vue';
44
+ import GoogleMap from 'components/base/GoogleMap.vue';
45
+ import Button from 'components/base/Button.vue';
46
+
47
+ const Modal = defineAsyncComponent(() => import('components/base/Modal.vue'));
48
+ const props = defineProps<{
49
+ modelValue?: ILocationInfo;
50
+ title?: string;
51
+ }>();
52
+
53
+ const emit = defineEmits<{
54
+ (e: 'update:modelValue', value: ILocationInfo | undefined): void;
55
+ }>();
56
+
57
+ const location = computed<ILocationInfo | undefined>({
58
+ get: () => props.modelValue,
59
+ set: (value: ILocationInfo | undefined) => emit('update:modelValue', value),
60
+ });
61
+ const isMapOpen = ref(false);
62
+ const isOpenDeleteModal = ref(false);
63
+
64
+ function toggleMap(value: boolean) {
65
+ isMapOpen.value = value;
66
+ }
67
+
68
+ function deleteLocation() {
69
+ location.value = undefined;
70
+ isOpenDeleteModal.value = false;
71
+ }
72
+ </script>