cja-phoenix 0.2.7 → 0.2.9

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.
@@ -0,0 +1,460 @@
1
+ <template>
2
+ <div
3
+ class="cross-sell-container"
4
+ v-if="activeViewport.lg && !loading.messages"
5
+ >
6
+ <Transition>
7
+ <div class="cross-sell-wrapper" v-if="step == 1">
8
+ <h2>{{ t("xsell.checkout.title") }}</h2>
9
+ <p v-html="t('xsell.checkout.description')"></p>
10
+
11
+ <div class="options-container">
12
+ <TileCheckboxInput
13
+ v-for="vert in crossSellOptions"
14
+ :class="[`checkbox-${vert.class}`]"
15
+ :image="vert.image"
16
+ :label="vert.label"
17
+ :description="vert.description"
18
+ :model-value="crossSellData[vert.control]"
19
+ @update:model-value="(v) => (crossSellData[vert.control] = v)"
20
+ />
21
+ </div>
22
+
23
+ <div class="btn-container">
24
+ <CjaButton :color="'orange'" @click="submit">
25
+ {{ t("xsell.checkout.btn.submit") }}
26
+ </CjaButton>
27
+ </div>
28
+ </div>
29
+ <div class="cross-sell-wrapper" v-else>
30
+ <div class="icon-wrapper">
31
+ <div class="success-icon m-cgg-icon--check2"></div>
32
+ </div>
33
+ <h2>{{ t("xsell.checkout.success.title") }}</h2>
34
+ <p>{{ t("xsell.checkout.success.description") }}</p>
35
+ </div>
36
+ </Transition>
37
+ </div>
38
+ <Scaffold v-else-if="!loading.messages">
39
+ <div class="cross-sell-banner" v-if="mobileBannerActive">
40
+ <div class="banner-header">
41
+ <span v-html="t('xsell.checkout.banner.title')"></span>
42
+ <button
43
+ class="btn-close m-cgg-icon--cross2"
44
+ @click="mobileBannerActive = false"
45
+ ></button>
46
+ </div>
47
+ <div class="banner-description">
48
+ <span class="m-cgg-icon--comparaja">
49
+ <span class="path1"></span>
50
+ <span class="path2"></span>
51
+ </span>
52
+ <span v-html="t('xsell.checkout.banner.description')"></span>
53
+ </div>
54
+ <div class="btn-container">
55
+ <CjaButton @click="openModal">
56
+ {{ t("xsell.checkout.banner.btn") }}
57
+ </CjaButton>
58
+ </div>
59
+ </div>
60
+ <Modal ref="modalEl">
61
+ <template v-slot:body>
62
+ <div class="cross-sell-modal-content" v-if="step == 1">
63
+ <h3>{{ t("xsell.checkout.modal.body.title") }}</h3>
64
+ <p>{{ t("xsell.checkout.modal.body.description") }}</p>
65
+ <div class="options-container">
66
+ <TileCheckboxInput
67
+ v-for="vert in crossSellOptions"
68
+ :class="[`checkbox-${vert.class}`]"
69
+ :image="vert.image"
70
+ :label="vert.label"
71
+ :description="vert.description"
72
+ :model-value="crossSellData[vert.control]"
73
+ @update:model-value="(v) => (crossSellData[vert.control] = v)"
74
+ />
75
+ </div>
76
+ </div>
77
+ <div class="cross-sell-modal-content" v-else>
78
+ <div class="icon-wrapper">
79
+ <div class="success-icon m-cgg-icon--check2"></div>
80
+ </div>
81
+ <div class="success-text">
82
+ <h3>{{ t("xsell.checkout.success.title") }}</h3>
83
+ <p>{{ t("xsell.checkout.success.description") }}</p>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ <template v-slot:footer>
88
+ <div class="modal-btn-container">
89
+ <CjaButton @click="submit">
90
+ {{ t("xsell.checkout.modal.footer.btn") }}
91
+ </CjaButton>
92
+ </div>
93
+ </template>
94
+ </Modal>
95
+ </Scaffold>
96
+ </template>
97
+
98
+ <script lang="ts" setup>
99
+ import { ref, watch, inject, onBeforeMount, computed } from "vue";
100
+ import { I18nOptions, useI18n } from "vue-i18n";
101
+ import TileCheckboxInput from "../forms/TileCheckboxInput.vue";
102
+ import Scaffold from "../structural/Scaffold.vue";
103
+ import Modal from "../structural/Modal.vue";
104
+ import CjaButton from "../structural/CjaButton.vue";
105
+
106
+ const props = defineProps<{
107
+ activeVertical?: string;
108
+ journeyId: string | undefined;
109
+ formData: {
110
+ name: string | undefined;
111
+ phone: string | undefined;
112
+ email: string | undefined;
113
+ marketingOptIn: boolean | undefined;
114
+ };
115
+ }>();
116
+
117
+ const loading = ref({
118
+ messages: true,
119
+ });
120
+ const activeViewport: any = inject("activeViewport");
121
+
122
+ const { t, setLocaleMessage } = useI18n({
123
+ legacy: false,
124
+ locale: "PT",
125
+ fallbackLocale: "PT",
126
+ messages: {},
127
+ missingWarn: false,
128
+ warnHtmlMessage: false,
129
+ useScope: "local",
130
+ });
131
+
132
+ const step = ref(1);
133
+
134
+ const crossSellOptions = computed(() => [
135
+ {
136
+ class: "all",
137
+ control: "all",
138
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-cpj.svg",
139
+ label: t("xsell.checkout.input.all.title"),
140
+ description: t("xsell.checkout.input.all.description"),
141
+ },
142
+ {
143
+ class: "hl",
144
+ control: "hl",
145
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-hl.svg",
146
+ label: t("xsell.checkout.input.hl.title"),
147
+ description: t("xsell.checkout.input.hl.description"),
148
+ },
149
+ {
150
+ class: "pl",
151
+ control: "pl",
152
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-pl.svg",
153
+ label: t("xsell.checkout.input.pl.title"),
154
+ description: t("xsell.checkout.input.pl.description"),
155
+ },
156
+ {
157
+ class: "cc",
158
+ control: "cc",
159
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-cc.svg",
160
+ label: t("xsell.checkout.input.cc.title"),
161
+ description: t("xsell.checkout.input.cc.description"),
162
+ },
163
+ {
164
+ class: "pl",
165
+ control: "csc",
166
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-csc.svg",
167
+ label: t("xsell.checkout.input.csc.title"),
168
+ description: t("xsell.checkout.input.csc.description"),
169
+ },
170
+ {
171
+ class: "ci",
172
+ control: "ci",
173
+ image: process.env.VUE_APP_IMG_URL + "xsell-checkout-in.svg",
174
+ label: t("xsell.checkout.input.ci.title"),
175
+ description: t("xsell.checkout.input.ci.description"),
176
+ },
177
+ ]);
178
+
179
+ const crossSellData = ref({
180
+ all: true,
181
+ hl: false,
182
+ pl: false,
183
+ cc: false,
184
+ csc: false,
185
+ ci: false,
186
+ });
187
+
188
+ const modalEl = ref();
189
+ const mobileBannerActive = ref(true);
190
+
191
+ onBeforeMount(() => {
192
+ fetch("/Internationalization?contains=xsell.checkout")
193
+ .then((response) => response.json())
194
+ .then((data: I18nOptions["messages"]) => {
195
+ if (data) {
196
+ loading.value.messages = false;
197
+ setLocaleMessage("PT", data);
198
+ }
199
+ });
200
+ });
201
+
202
+ watch(
203
+ () => Object.entries(crossSellData.value),
204
+ (newVal, oldVal) => {
205
+ const newObj = Object.fromEntries(newVal);
206
+ const oldObj = Object.fromEntries(oldVal);
207
+
208
+ if (newObj.all && !oldObj.all) {
209
+ for (const v in crossSellData.value) {
210
+ if (v != "all") {
211
+ crossSellData.value[v] = false;
212
+ }
213
+ }
214
+ } else if (newVal.filter((v) => v[0] != "all" && v[1]).length) {
215
+ crossSellData.value.all = false;
216
+ }
217
+ }
218
+ );
219
+
220
+ const openModal = () => {
221
+ step.value = 1;
222
+ crossSellData.value = {
223
+ all: true,
224
+ hl: false,
225
+ pl: false,
226
+ cc: false,
227
+ csc: false,
228
+ ci: false,
229
+ };
230
+ modalEl.value.openModal();
231
+ };
232
+
233
+ const submit = () => {
234
+ const payload = Object.entries(crossSellData.value)
235
+ .filter((v) => v[1])
236
+ .map((v) => {
237
+ switch (v[0]) {
238
+ case "csc":
239
+ return "PL";
240
+ case "all":
241
+ return "BLOG";
242
+ default:
243
+ return v[0].toUpperCase();
244
+ }
245
+ });
246
+
247
+ fetch(new URL("/core/apis/data/updateForm", process.env.VUE_APP_API_URL), {
248
+ method: "PUT",
249
+ body: JSON.stringify({
250
+ journeyId: props.journeyId,
251
+ eventType: "LEAD_CAPTURE",
252
+ isActionEvent: true,
253
+ stepName: "LEAD_CAPTURE",
254
+ isCompleted: true,
255
+ sentToDialer: false,
256
+ lastStepNumber: 0,
257
+ lastStepType: "LEAD_CAPTURE",
258
+ lastStepUrl:
259
+ window.location.pathname +
260
+ window.location.search +
261
+ window.location.hash,
262
+ data: JSON.stringify({ verticalCodeSelector: payload }),
263
+ }),
264
+ });
265
+
266
+ fetch(new URL("/core/apis/data/captureLead", process.env.VUE_APP_API_URL), {
267
+ method: "POST",
268
+ body: JSON.stringify({
269
+ name: props.formData.name,
270
+ phone: props.formData.phone,
271
+ email: props.formData.email,
272
+ verticalCode: payload.length == 1 ? payload[0] : "BLOG",
273
+ utmSource: "RESULTS_PAGE",
274
+ verticalCodeSelector: payload,
275
+ sentToSalesforce: props.formData.marketingOptIn,
276
+ sentToDialer: false,
277
+ fromBlog: true,
278
+ xSell: true,
279
+ }),
280
+ });
281
+
282
+ step.value = 2;
283
+
284
+ if (!activeViewport.value.lg) {
285
+ setTimeout(() => {
286
+ modalEl.value.closeModal();
287
+ mobileBannerActive.value = false;
288
+ }, 3000);
289
+ }
290
+ };
291
+ </script>
292
+
293
+ <style lang="scss" scoped>
294
+ .cross-sell-container {
295
+ padding-top: 40px;
296
+ margin-top: 40px;
297
+ border-top: 1px solid #dedede;
298
+
299
+ .cross-sell-wrapper {
300
+ max-width: 1050px;
301
+ margin: 0 auto;
302
+ }
303
+
304
+ h2 {
305
+ margin: 0 0 25px;
306
+ font-weight: 700;
307
+ font-size: 24px;
308
+ line-height: 27px;
309
+ text-align: center;
310
+ color: #155072;
311
+ }
312
+
313
+ p {
314
+ margin: 0 0 30px;
315
+ font-size: 18px;
316
+ line-height: 21px;
317
+ text-align: center;
318
+ }
319
+
320
+ .btn-container {
321
+ display: flex;
322
+ justify-content: center;
323
+ margin-top: 40px;
324
+ }
325
+ }
326
+
327
+ .options-container {
328
+ display: flex;
329
+ flex-direction: column;
330
+ gap: 10px;
331
+
332
+ @media screen and (min-width: 992px) {
333
+ flex-direction: row;
334
+ flex-wrap: wrap;
335
+ justify-content: center;
336
+ gap: 30px;
337
+ }
338
+
339
+ [class*="checkbox-"] {
340
+ @media screen and (min-width: 992px) {
341
+ flex-basis: 225px;
342
+ }
343
+ }
344
+
345
+ .checkbox-hl:not(.checked) {
346
+ background: linear-gradient(75.75deg, #f1f7ee 1.02%, #ffffff 100%);
347
+ }
348
+
349
+ .checkbox-pl:not(.checked) {
350
+ background: linear-gradient(80.77deg, #f4f9fc 1.02%, #ffffff 100%), #ffffff;
351
+ }
352
+
353
+ .checkbox-cc:not(.checked) {
354
+ background: linear-gradient(80.77deg, #fff7ff 1.02%, #ffffff 100%), #ffffff;
355
+ }
356
+
357
+ .checkbox-ci:not(.checked) {
358
+ background: linear-gradient(80.77deg, #f6f4ff 1.02%, #ffffff 100%), #ffffff;
359
+ }
360
+ }
361
+
362
+ .cross-sell-banner {
363
+ position: fixed;
364
+ left: 0;
365
+ bottom: 0;
366
+ width: 100%;
367
+ padding: 18px 16px;
368
+ background-color: #fff;
369
+ box-shadow: 0px -4px 13px rgba(0, 0, 0, 0.1);
370
+
371
+ .banner-header {
372
+ position: relative;
373
+ display: flex;
374
+ justify-content: center;
375
+ gap: 15px;
376
+ font-size: 16px;
377
+ line-height: 16px;
378
+ margin-bottom: 15px;
379
+
380
+ span {
381
+ flex-basis: 360px;
382
+ }
383
+
384
+ .btn-close {
385
+ position: absolute;
386
+ right: 0;
387
+ top: 50%;
388
+ transform: translateY(-50%);
389
+ background: none;
390
+ padding: 0;
391
+ border: none;
392
+ outline: none;
393
+ font-size: 24px;
394
+ }
395
+ }
396
+
397
+ .banner-description {
398
+ max-width: 360px;
399
+ margin: 0 auto;
400
+ display: flex;
401
+ flex-direction: row;
402
+ gap: 15px;
403
+ font-size: 14px;
404
+ line-height: 21px;
405
+ margin-bottom: 20px;
406
+
407
+ .m-cgg-icon--comparaja {
408
+ font-size: 30px;
409
+ }
410
+ }
411
+
412
+ .btn-container {
413
+ display: grid;
414
+ max-width: 360px;
415
+ margin: 0 auto;
416
+ }
417
+ }
418
+
419
+ .cross-sell-modal-content {
420
+ h3 {
421
+ font-weight: 700;
422
+ font-size: 24px;
423
+ line-height: 29px;
424
+ margin: 10px 0 15px;
425
+ }
426
+
427
+ p {
428
+ font-size: 16px;
429
+ line-height: 24px;
430
+ margin: 0 0 25px;
431
+ }
432
+
433
+ .success-text {
434
+ text-align: center;
435
+ }
436
+ }
437
+
438
+ .modal-btn-container {
439
+ display: grid;
440
+ }
441
+
442
+ .icon-wrapper {
443
+ margin: 0 auto;
444
+
445
+ .success-icon {
446
+ display: flex;
447
+ align-items: center;
448
+ justify-content: center;
449
+ border-radius: 50%;
450
+ color: #fff;
451
+ font-weight: 700;
452
+ user-select: none;
453
+ background-color: #77aa43;
454
+ height: 80px;
455
+ width: 80px;
456
+ margin: 0 auto 30px;
457
+ font-size: 60px;
458
+ }
459
+ }
460
+ </style>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <GridContainer class="checkout-container">
3
+ <GridItem
4
+ class="checkout-wrapper"
5
+ :size-sm="2"
6
+ :size-md="4"
7
+ :size-lg="$slots.sidebar ? 8 : 12"
8
+ >
9
+ <slot name="content"></slot>
10
+ </GridItem>
11
+ <GridItem
12
+ class="checkout-sidebar"
13
+ v-if="activeViewport.lg && $slots.sidebar"
14
+ :size-lg="4"
15
+ :style="{ background: sidebarBackground }"
16
+ >
17
+ <slot name="sidebar"></slot>
18
+ </GridItem>
19
+ </GridContainer>
20
+ </template>
21
+
22
+ <script lang="ts" setup>
23
+ import { inject } from "vue";
24
+ import GridContainer from "../structural/GridContainer.vue";
25
+ import GridItem from "../structural/GridItem.vue";
26
+
27
+ const activeViewport: any = inject("activeViewport");
28
+
29
+ defineProps<{
30
+ sidebarBackground: string;
31
+ }>();
32
+ </script>
33
+
34
+ <style lang="scss" scoped>
35
+ .checkout-container {
36
+ .checkout-wrapper {
37
+ max-width: 750px;
38
+ padding: 32px 16px;
39
+ margin: 0 auto;
40
+
41
+ @media screen and (min-width: 1024px) {
42
+ padding: 56px 0;
43
+ }
44
+ }
45
+
46
+ .checkout-sidebar {
47
+ padding: 32px 16px;
48
+ border-top: 1px solid #dedede;
49
+
50
+ @media screen and (min-width: 992px) {
51
+ padding: 56px 0 56px 24px;
52
+ box-shadow: -10px 0 10px rgba(0, 0, 0, 0.1);
53
+ border-top: none;
54
+ }
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,149 @@
1
+ <template>
2
+ <div class="checkout-milestones">
3
+ <ul>
4
+ <li
5
+ v-for="(milestone, i) in milestones"
6
+ :class="[`milestone-${milestone.status}`]"
7
+ >
8
+ <span
9
+ class="step-icon"
10
+ :class="{ 'm-cgg-icon--check2': milestone.status == 'past' }"
11
+ >
12
+ {{ milestone.status != "past" ? i + 1 : "" }}
13
+ </span>
14
+ <div class="text-container">
15
+ <div class="title">
16
+ {{ milestone.title }}
17
+ </div>
18
+ <div class="description" v-if="milestone.description">
19
+ {{ milestone.description }}
20
+ </div>
21
+ </div>
22
+ </li>
23
+ </ul>
24
+ </div>
25
+ </template>
26
+
27
+ <script lang="ts" setup>
28
+ import { CheckoutMilestone } from "../../types";
29
+
30
+ defineProps<{
31
+ milestones: CheckoutMilestone[];
32
+ }>();
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ .checkout-milestones {
37
+ ul {
38
+ list-style: none;
39
+ padding: 0;
40
+ margin: 0 auto;
41
+ max-width: 500px;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 35px;
45
+
46
+ li {
47
+ position: relative;
48
+ display: flex;
49
+ gap: 15px;
50
+ padding: 0 16px;
51
+
52
+ .step-icon {
53
+ position: relative;
54
+ z-index: 1;
55
+ display: flex;
56
+ justify-content: center;
57
+ align-items: center;
58
+ width: 25px;
59
+ height: 25px;
60
+ border-radius: 50%;
61
+ color: #fff;
62
+ font-weight: 700;
63
+ font-size: 12px;
64
+ line-height: 13px;
65
+ flex-shrink: 0;
66
+ }
67
+
68
+ .text-container {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 8px;
72
+
73
+ .title {
74
+ font-size: 20px;
75
+ line-height: 24px;
76
+ font-weight: 700;
77
+ }
78
+
79
+ .description {
80
+ font-size: 16px;
81
+ line-height: 19px;
82
+ }
83
+ }
84
+
85
+ &.milestone-past,
86
+ &.milestone-future {
87
+ &::before,
88
+ &::after {
89
+ position: absolute;
90
+ left: 27px;
91
+ width: 3px;
92
+ }
93
+ }
94
+
95
+ &.milestone-past {
96
+ &::after {
97
+ content: "";
98
+ top: 0;
99
+ background-color: #77aa43;
100
+ height: calc(100% + 35px);
101
+ }
102
+
103
+ .step-icon {
104
+ background-color: #77aa43;
105
+ }
106
+ }
107
+
108
+ &.milestone-future {
109
+ &::before,
110
+ &::after {
111
+ content: "";
112
+ background-color: #9fabbc;
113
+ }
114
+
115
+ &::before {
116
+ top: -35px;
117
+ height: 35px;
118
+ }
119
+
120
+ &:not(:last-child)::after {
121
+ top: 0;
122
+ height: 100%;
123
+ }
124
+
125
+ .step-icon {
126
+ background-color: #9fabbc;
127
+ }
128
+ }
129
+
130
+ &.milestone-current {
131
+ padding: 15px;
132
+ background: #ffffff;
133
+ box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.1);
134
+ border: 1px solid #dedede;
135
+ border-radius: 16px;
136
+ color: #076b9c;
137
+
138
+ .step-icon {
139
+ background-color: #076b9c;
140
+
141
+ &::after {
142
+ display: none;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ </style>
@@ -35,7 +35,7 @@ defineEmits(["btn:previous"]);
35
35
  .results-container {
36
36
  .results-grid {
37
37
  padding-top: 24px;
38
- padding-bottom: 24px;
38
+ padding-bottom: 16px;
39
39
  }
40
40
 
41
41
  .back-container {
@@ -47,7 +47,7 @@ defineEmits(["btn:previous"]);
47
47
  gap: 5px;
48
48
  font-weight: 700;
49
49
  font-size: 15px;
50
- line-height: 33px;
50
+ line-height: 29px;
51
51
  cursor: pointer;
52
52
  border: none;
53
53
  padding: 0;
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <FixedContainer
3
3
  ref="fixedContainer"
4
- :scrollThreshold="-24"
5
- :position="{ top: '24px', bottom: '24px' }"
4
+ :scrollThreshold="-16"
5
+ :position="{ top: '16px', bottom: '16px' }"
6
6
  >
7
7
  <div
8
8
  ref="sidebarContainer"
@@ -46,7 +46,7 @@ withDefaults(
46
46
 
47
47
  const setSidebarHeight = () => {
48
48
  sidebarMaxHeight.value = `${
49
- window.innerHeight - sidebarContainer.value.offsetTop - 24 + window.scrollY
49
+ window.innerHeight - sidebarContainer.value.offsetTop - 16 + window.scrollY
50
50
  }px`;
51
51
  };
52
52