project-booster-vue 9.41.1 → 9.42.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-booster-vue",
3
- "version": "9.41.1",
3
+ "version": "9.42.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -6,8 +6,7 @@
6
6
  :left-icon="BACK_ICON"
7
7
  :class="{
8
8
  'pb-products__back-button': true,
9
- 'pb-products__back-button--hidden':
10
- !showBackButton && !decorate(answers, runtimeOptions, payload.viewModel.forceBackButton),
9
+ 'pb-products__back-button--hidden': true,
11
10
  }"
12
11
  @click.once="$emit('go-back')"
13
12
  />
@@ -23,49 +23,67 @@ import CALL_TO_ACTION_WITH_MODAL from './restitution-call-to-actions-with-modal.
23
23
  />
24
24
 
25
25
  export const summary = {
26
- subprojectId: 'ae62d167-e02e-4744-81b1-102cc5816be2',
27
- subprojectTemplateId: '22248ac3-9391-47f4-8a54-6fe280c2fa5b',
26
+ subprojectTemplateId: '44cddf9d-f0e8-4d3d-82dc-d717dc70342a',
27
+ subprojectTemplateLabel: 'Pompe à chaleur air-eau',
28
28
  businessUnit: 'LMFR',
29
- summaryDate: '2020-05-13T09:49:45.886602047Z',
30
- subprojectTemplateLabel: '<strong>Pompe à chaleur air/air</strong>',
29
+ summaryDate: '2022-10-13T12:24:39.316059261Z',
31
30
  cost: {
32
- min: 3139.99,
33
- max: 3939.99,
31
+ min: 12103.78,
32
+ max: 19103.78,
34
33
  currency: 'EURO',
35
34
  },
36
35
  components: [
37
36
  {
38
- componentId: 'LAND',
39
- title: '<strong>Pompe à chaleur installée</strong> (hors aides)',
37
+ componentId: 'FURNISHING',
38
+ title: 'Pompe à chaleur installée (hors aides)',
39
+ details: 'Travaux réalisés par nos artisans partenaires Leroy Merlin',
40
40
  cost: {
41
- min: 3139.99,
42
- max: 3939.99,
41
+ min: 13000,
42
+ max: 20000,
43
43
  currency: 'EURO',
44
44
  },
45
- details: 'Test du message vert',
46
45
  lines: [
47
46
  {
48
- text: '<strong>Pompe à chaleur installée</strong> (hors aides)',
49
- details: 'Terrain constructible, délimité et viabilisé. La nature du sous-sol est garantie.',
47
+ text: 'Pompe à chaleur',
50
48
  cost: {
51
- min: 6499,
52
- max: 7299,
49
+ min: 10000,
50
+ max: 15000,
53
51
  currency: 'EURO',
54
52
  },
55
53
  },
56
54
  {
57
- text: '<strong>Pompe à chaleur installée</strong> (hors aides)',
55
+ text: 'Installation',
58
56
  cost: {
59
- min: 6499,
60
- max: 7299,
57
+ min: 3000,
58
+ max: 5000,
59
+ currency: 'EURO',
60
+ },
61
+ },
62
+ ],
63
+ },
64
+ {
65
+ componentId: 'AID',
66
+ title: 'Aides estimées (MaPrimeRénov, Prime Energie)',
67
+ details: 'Les aides varient selon certains critères.',
68
+ cost: {
69
+ min: -896.22,
70
+ max: -950.22,
71
+ currency: 'EURO',
72
+ },
73
+ lines: [
74
+ {
75
+ text: 'Estimation Prime énergie Leroy Merlin',
76
+ cost: {
77
+ min: -96.22,
78
+ max: -650.22,
61
79
  currency: 'EURO',
62
80
  },
63
81
  },
64
82
  {
65
- text: '<strong>Aide estimées</strong> (MaPrimeRénov, Prime Energie)',
66
- type: 'REDUCTION',
83
+ text: 'Estimation MaPrimeRénov',
67
84
  cost: {
68
- reduce: -3600,
85
+ min: -800,
86
+ max: -900,
69
87
  currency: 'EURO',
70
88
  },
71
89
  },
@@ -73,21 +91,15 @@ export const summary = {
73
91
  },
74
92
  ],
75
93
  subprojectAttributes: {
76
- finish: 'PARTIALLY_BY_MYSELF',
77
- houseType: 'CONTEMPORARY',
78
- inseeCode: '59350',
79
- land: 'NOT_YET',
80
- landSurfaceInSquareMeters: 123,
81
- landType: 'IN_A_LOT',
82
- livingAreaInSquareMeters: 465,
94
+ age: 'NEW',
95
+ housingType: 'HOUSE',
96
+ income: 'MIDDLE',
97
+ inhabitantsCount: 1,
98
+ location: '20137',
99
+ situation: 'OWNER',
100
+ surfaceAreaInSquareMeters: 40,
83
101
  },
84
102
  suggestedTypologies: [
85
- {
86
- domesticSpaceLabel: 'Cuisine',
87
- domesticSpaceHref: '/domestic-spaces/59405',
88
- projectKindHref: '/project-kinds/34048',
89
- projectTypeHref: '/project-types/29239',
90
- },
91
103
  {
92
104
  domesticSpaceLabel: 'Tout le logement',
93
105
  domesticSpaceHref: '/domestic-spaces/59461',
@@ -95,11 +107,6 @@ export const summary = {
95
107
  projectTypeHref: '/project-types/61855',
96
108
  },
97
109
  ],
98
- legalMentions: [
99
- "Les prix ci-dessus ne sont proposés qu'à titre indicatif et sont basés sur les prix du marché.",
100
- "L'outil d'estimation ne vaut pas offre de vente.",
101
- "La disponibilité des produits au moment de l'achat n'est pas garantie.",
102
- ],
103
110
  };
104
111
 
105
112
  # 🦠 `PbRestitutionList` - Component
@@ -74,53 +74,36 @@
74
74
  <div class="pb-restitution-list__modal__details__title" v-html="summary.subprojectTemplateLabel"></div>
75
75
 
76
76
  <div class="pb-restitution-list__modal__details__row">
77
- <div class="pb-restitution-list__modal__details__row__title" justify-content="space-between">
78
- <div v-html="summary.components[0].title"></div>
79
- <div>
80
- <strong>{{ summary.components[0].cost.min }}€ - {{ summary.components[0].cost.max }}€</strong>
77
+ <div v-for="component in summary.components" :key="component.details">
78
+ <div class="pb-restitution-list__modal__details__row__title" justify-content="space-between">
79
+ <div v-html="component.title"></div>
80
+ <div v-if="component.cost.min != component.cost.max">
81
+ <strong>{{ component.cost.min }}€ et {{ component.cost.max }}€</strong>
82
+ </div>
83
+ <div v-else>
84
+ <strong>{{ component.cost.min }}€</strong>
85
+ </div>
81
86
  </div>
82
- </div>
83
87
 
84
- <div v-for="line in summary.components" :key="line.details">
85
- <div class="pb-restitution-list__modal__details__row__label" v-if="line.details">
86
- {{ line.details }}
88
+ <div class="pb-restitution-list__modal__details__row__label" v-if="component.details">
89
+ {{ component.details }}
87
90
  </div>
88
- </div>
89
-
90
- <div
91
- v-for="line in summary.components[0].lines"
92
- :key="line.text"
93
- class="pb-restitution-list__modal__details__row__list"
94
- :class="{ ' pb-restitution-list__modal__details__row': line.cost.reduce }"
95
- >
96
- <ul>
97
- <div>
98
- <li v-if="!line.cost.reduce">
99
- <div v-html="line.text" v-if="!line.cost.reduce"></div>
100
- <div v-if="!line.cost.reduce">{{ line.cost.min }}€ - {{ line.cost.max }}€</div>
101
- </li>
102
- </div>
103
- </ul>
104
- </div>
105
91
 
106
- <div v-for="line in summary.components[0].lines" :key="line.text">
107
92
  <div
108
- v-if="line.cost.reduce"
109
- class="pb-restitution-list__modal__details__row__title"
110
- :class="{ ' help border-bottom': line.cost.reduce }"
111
- justify-content="space-between"
93
+ v-for="line in component.lines"
94
+ :key="line.text"
95
+ class="pb-restitution-list__modal__details__row__list"
96
+ :class="{ ' pb-restitution-list__modal__details__row': line.cost.reduce }"
112
97
  >
113
- <span :class="{ hidden: !line.cost.reduce }">
114
- <div v-if="line.cost.reduce">
115
- <div v-html="line.text" class="green"></div>
98
+ <ul>
99
+ <div>
100
+ <li>
101
+ <div v-html="line.text"></div>
102
+ <div v-if="line.cost.min != line.cost.max">{{ line.cost.min }}€ et {{ line.cost.max }}€</div>
103
+ <div v-else>{{ line.cost.min }}€</div>
104
+ </li>
116
105
  </div>
117
- </span>
118
-
119
- <div v-for="line in summary.components[0].lines" :key="line">
120
- <div v-if="line.cost.reduce" class="green">
121
- <strong>{{ line.cost.reduce }}€</strong>
122
- </div>
123
- </div>
106
+ </ul>
124
107
  </div>
125
108
  </div>
126
109
  </div>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <m-flex align-items="center" direction="column">
2
+ <m-flex align-items="center" direction="column" v-if="payload.componentId != 'AID'">
3
3
  <m-flex :class="`pb-restitution-list-block`" direction="column" ref="pbRestitutionListBlock">
4
4
  <div class="pb-restitution-list-block__title" v-html="summary.subprojectTemplateLabel"></div>
5
5
 
@@ -7,7 +7,7 @@
7
7
  <div class="pb-restitution-list-line">
8
8
  <div class="pb-restitution-list-line__title" v-html="summary.components[0].title"></div>
9
9
  <div class="pb-restitution-list-line__price">
10
- <div class="pb-restitution-list-line__price__min">{{ summary.components[0].cost.min }}€&nbsp;-&nbsp;</div>
10
+ <div class="pb-restitution-list-line__price__min">{{ summary.components[0].cost.min }}€&nbsp;et&nbsp;</div>
11
11
  <div class="pb-restitution-list-line__price__max">{{ summary.components[0].cost.max }}€</div>
12
12
  </div>
13
13
  </div>
@@ -19,6 +19,17 @@
19
19
  :line="line"
20
20
  ></pb-restitution-list-line>
21
21
 
22
+ <div v-for="aidComponent in summary.components" :key="aidComponent.componentId">
23
+ <div v-if="aidComponent.componentId === 'AID'">
24
+ <pb-restitution-list-line
25
+ :key="aidComponent.title"
26
+ :component="aidComponent"
27
+ :line="aidComponent"
28
+ :renovAid="true"
29
+ ></pb-restitution-list-line>
30
+ </div>
31
+ </div>
32
+
22
33
  <div class="pb-restitution-list-block__body__row unbordered">
23
34
  <div class="pb-restitution-list-block__body__row__title full">
24
35
  <div><strong>Montant total</strong> (aides déduites)</div>
@@ -1,13 +1,16 @@
1
1
  <template>
2
- <div class="pb-restitution-list-line" :class="{ green: line.type === 'REDUCTION' }" v-if="line.type">
3
- <div class="pb-restitution-list-line__title" v-html="line.text"></div>
4
- <div class="pb-restitution-list-line__price" v-if="line.type != 'REDUCTION'">
2
+ <div class="pb-restitution-list-line" :class="{ green: renovAid }" v-if="renovAid">
3
+ <div class="pb-restitution-list-line__title" v-html="line.text || line.title"></div>
4
+ <div class="pb-restitution-list-line__price" v-if="!renovAid">
5
5
  <div class="pb-restitution-list-line__price__min">
6
- {{ formatPriceRange(line.cost.min, line.cost.max).min }}€&nbsp;-&nbsp;
6
+ {{ formatPriceRange(line.cost.min, line.cost.max).min }}€&nbsp;et&nbsp;
7
7
  </div>
8
8
  <div class="pb-restitution-list-line__price__max">{{ formatPriceRange(line.cost.min, line.cost.max).max }}€</div>
9
9
  </div>
10
- <div class="pb-restitution-list-line__price">{{ line.cost.reduce }}€</div>
10
+ <div class="pb-restitution-list-line__price">
11
+ <span v-if="line.cost.min === line.cost.max">{{ line.cost.min }}€</span>
12
+ <span v-else>{{ line.cost.min }} et {{ line.cost.max }}€</span>
13
+ </div>
11
14
  </div>
12
15
  </template>
13
16
 
@@ -24,6 +27,10 @@ export default defineComponent({
24
27
  type: Object,
25
28
  default: () => ({}),
26
29
  },
30
+ renovAid: {
31
+ type: Boolean,
32
+ default: () => false,
33
+ },
27
34
  },
28
35
  methods: {
29
36
  handleLinkClick() {
@@ -0,0 +1,47 @@
1
+ import { nestedAppDecorator } from '../../../.storybook/nested-app-decorator';
2
+ import { ArgsTable, Canvas, Meta, Source, Story } from '@storybook/addon-docs';
3
+ import cloneDeep from 'lodash.clonedeep';
4
+ import PbTrezor from './PbTrezor';
5
+ import DEFAULT_PAYLOAD from './default-payload.json';
6
+
7
+ <Meta
8
+ title="Project Booster/Components/Trezor/PbTrezor 🦠"
9
+ component={PbTrezor}
10
+ decorators={[nestedAppDecorator({}, [])]}
11
+ parameters={{ layout: 'fullscreen' }}
12
+ />
13
+
14
+ # 🦠 `PbTrezor` - Components
15
+
16
+ [![alt text](https://storage.googleapis.com/project-booster-media/project-booster-vue/project-booster-sources.svg 'Project booster component source code')](https://github.com/adeo/project-booster-vue/tree/master/src/components/question)
17
+
18
+ ---
19
+
20
+ The `PbTrezor` Vue component allows select family amount persons. It
21
+ is customizable through the payload property.
22
+
23
+ It has a default provided payload, used in the Sandbox below.
24
+
25
+ The input field is automatically focused.
26
+
27
+ # `PbTrezor`
28
+
29
+ export const TemplateSandbox = (args, { argTypes }) => ({
30
+ props: Object.keys(argTypes),
31
+ components: { PbTrezor },
32
+ setup() {
33
+ return { args };
34
+ },
35
+ template: `<pb-trezor
36
+ :payload="args.payload"
37
+ :show-back-button="args.showBackButton"
38
+ :completed-event-name="args.completedEventName"
39
+ @step-completed="args.onStepCompleted"
40
+ />`,
41
+ });
42
+
43
+ <Canvas>
44
+ <Story name="101 Sandbox" args={{ payload: DEFAULT_PAYLOAD }}>
45
+ {TemplateSandbox.bind({})}
46
+ </Story>
47
+ </Canvas>
@@ -0,0 +1,356 @@
1
+ <template>
2
+ <m-flex class="pb-trezor" direction="column">
3
+ <div class="pb-trezor__title" v-if="payload?.viewModel?.label">
4
+ {{ payload.viewModel.label }}
5
+ </div>
6
+
7
+ <form action="" class="pb-trezor__form">
8
+ <m-flex class="pb-trezor__form__row">
9
+ <m-text-input
10
+ v-model="formData.values.lastname"
11
+ :error="formData.errors.lastname"
12
+ @focus="validate('lastname')"
13
+ @keypress="validate('lastname')"
14
+ type="text"
15
+ label="Nom"
16
+ :required="true"
17
+ class="pb-trezor__input"
18
+ ></m-text-input>
19
+ </m-flex>
20
+
21
+ <m-flex class="pb-trezor__form__row">
22
+ <m-text-input
23
+ v-model="formData.values.firstname"
24
+ :error="formData.errors.firstname"
25
+ @focus="validate('firstname')"
26
+ @keypress="validate('firstname')"
27
+ type="text"
28
+ label="Prénom"
29
+ :required="true"
30
+ class="pb-trezor__input"
31
+ ></m-text-input>
32
+ </m-flex>
33
+
34
+ <m-flex class="pb-trezor__form__row">
35
+ <m-text-input
36
+ v-model="formData.values.zipcode"
37
+ :error="formData.errors.zipcode"
38
+ @focus="validate('zipcode')"
39
+ @keypress="validate('zipcode')"
40
+ type="text"
41
+ label="Code postal"
42
+ :required="true"
43
+ class="pb-trezor__input"
44
+ ></m-text-input>
45
+ </m-flex>
46
+
47
+ <m-flex class="pb-trezor__form__row">
48
+ <m-text-input
49
+ v-model="formData.values.email"
50
+ :error="formData.errors.email"
51
+ @focus="validate('email')"
52
+ @keypress="validate('email')"
53
+ type="email"
54
+ label="E-mail"
55
+ :required="true"
56
+ class="pb-trezor__input"
57
+ ></m-text-input>
58
+ </m-flex>
59
+
60
+ <m-flex class="pb-trezor__form__row">
61
+ <m-text-input
62
+ v-model="formData.values.phone"
63
+ :error="formData.errors.phone"
64
+ @focus="validate('phone')"
65
+ @keypress="validate('phone')"
66
+ type="phone"
67
+ label="Numéro de téléphone"
68
+ :required="true"
69
+ class="pb-trezor__input"
70
+ ></m-text-input>
71
+ </m-flex>
72
+
73
+ <m-flex class="pb-trezor__form__row space" direction="column">
74
+ <label for="" class="pb-trezor__form__label"><span>- obligatoire</span></label>
75
+ <m-checkbox
76
+ class="pb-trezor__form__checkbox"
77
+ :required="true"
78
+ label="J'ai lu est accepte sans réserve les conditions générales d'utilisation"
79
+ v-model="formData.values.optin"
80
+ ></m-checkbox>
81
+ </m-flex>
82
+
83
+ <m-flex class="pb-trezor__form__row space" direction="column">
84
+ <m-checkbox
85
+ class="pb-trezor__form__checkbox"
86
+ :required="false"
87
+ v-model="formData.values.optinPartners"
88
+ label="J'autorise Leroy Merlin à transmettre les données collectées ici à l'un de nos partenaires susceptibles de réaliser le chantier (en fonction des critères géographiques)"
89
+ ></m-checkbox>
90
+ </m-flex>
91
+
92
+ <div class="pb-trezor__container">
93
+ <m-flex class="pb-trezor__container--button" direction="row">
94
+ <m-button
95
+ :theme="action.bordered ? 'bordered' : 'solid'"
96
+ class="pb-trezor__container--button__ok mc-button--full"
97
+ :type="action.type"
98
+ :label="action.label"
99
+ icon-position="right"
100
+ :href="action.href"
101
+ v-for="action in payload.callToActions"
102
+ :key="action.label"
103
+ @click.prevent="callAction(action)"
104
+ />
105
+ </m-flex>
106
+ </div>
107
+ </form>
108
+ </m-flex>
109
+ </template>
110
+
111
+ <script lang="ts">
112
+ import { defineComponent, PropType, ref } from 'vue';
113
+ import { useStore } from 'vuex';
114
+ import MFlex from './../mozaic/flex/MFlex.vue';
115
+ import MButton from '../mozaic/buttons/MButton.vue';
116
+ import MCheckbox from '../mozaic/checkbox/MCheckbox.vue';
117
+ import MTextInput from '../mozaic/text-input/MTextInput.vue';
118
+ import { ScenarioStepAnswer } from '@/types/pb/Scenario';
119
+ import { object, string, boolean } from 'yup';
120
+
121
+ export default defineComponent({
122
+ name: 'PbTrezor',
123
+ components: {
124
+ MFlex,
125
+ MButton,
126
+ MTextInput,
127
+ MCheckbox,
128
+ },
129
+ props: {
130
+ /**
131
+ * The component view model and business data as an object. The provided prop
132
+ * is merged with the default payload value so only overriden values will change
133
+ * from the default ones.
134
+ */
135
+ payload: {
136
+ type: Object,
137
+ default: () => ({}),
138
+ },
139
+ /**
140
+ * The options provided at runtime to customize component behaviour
141
+ */
142
+ runtimeOptions: {
143
+ type: Object,
144
+ default: () => ({}),
145
+ },
146
+ /**
147
+ * Indicates whether the back button should be displayed
148
+ */
149
+ showBackButton: {
150
+ type: Boolean,
151
+ default: true,
152
+ },
153
+ /**
154
+ * Name for the event to send when the step is questio is answered
155
+ */
156
+ completedEventName: {
157
+ type: String,
158
+ default: 'step-completed',
159
+ },
160
+ /**
161
+ * The previous answers to inject
162
+ */
163
+ answers: {
164
+ type: Object as PropType<Map<string, ScenarioStepAnswer[]>>,
165
+ default: () => new Map<string, ScenarioStepAnswer[]>(),
166
+ },
167
+ },
168
+ setup(props, { emit }) {
169
+ const store = useStore();
170
+ let formData = ref({
171
+ values: {
172
+ lastname: '',
173
+ firstname: '',
174
+ zipcode: '',
175
+ email: '',
176
+ phone: '',
177
+ optin: false,
178
+ optinPartners: false,
179
+ },
180
+ errors: {
181
+ lastname: '',
182
+ firstname: '',
183
+ zipcode: '',
184
+ email: '',
185
+ phone: '',
186
+ },
187
+ });
188
+
189
+ const validationSchema = object({
190
+ lastname: string().required(),
191
+ firstname: string().required(),
192
+ zipcode: string().required(),
193
+ email: string().email().required(),
194
+ phone: string().required(),
195
+ optin: boolean(),
196
+ optinPartners: boolean(),
197
+ });
198
+
199
+ const validate = async (field: string) => {
200
+ await validationSchema
201
+ .validateAt(field, formData.value.values)
202
+ .then(() => {
203
+ formData.value.errors[field] = '';
204
+ })
205
+ .catch((err) => {
206
+ formData.value.errors[err.path] = err.message;
207
+ });
208
+ };
209
+
210
+ const validateAllFields = async (action: any) => {
211
+ await validationSchema.validate(formData.value.values).then(async () => {
212
+ const sending = await store.dispatch(
213
+ 'trezor/sendFormData',
214
+ {
215
+ customer: {
216
+ lastname: formData.value.values.lastname,
217
+ firstname: formData.value.values.firstname,
218
+ zipcode: formData.value.values.zipcode,
219
+ email: formData.value.values.email,
220
+ phone: formData.value.values.phone,
221
+ },
222
+ optin: formData.value.values.optin,
223
+ optinPartners: formData.value.values.optinPartners,
224
+ },
225
+ props.payload.viewModel.typeLead,
226
+ );
227
+
228
+ if (sending) {
229
+ emit('step-completed', {
230
+ answers: [],
231
+ nextStep: action,
232
+ });
233
+ }
234
+ });
235
+ };
236
+
237
+ const callAction = async (item: any) => {
238
+ if (item.action.type === 'submit') {
239
+ await validateAllFields(item);
240
+ }
241
+ };
242
+
243
+ return {
244
+ formData,
245
+ callAction,
246
+ validate,
247
+ };
248
+ },
249
+ });
250
+ </script>
251
+
252
+ <style lang="scss" scoped>
253
+ @import 'pb-variables';
254
+ @import 'settings-tools/all-settings';
255
+ @import 'typography/_t.bodys';
256
+
257
+ $small-responsive-breakpoint: 's-large';
258
+ $responsive-breakpoint: 'm';
259
+
260
+ .pb-trezor {
261
+ margin: 0 auto;
262
+ max-width: 1024px;
263
+ &__title {
264
+ @include set-font-face('semi-bold');
265
+ @include set-font-scale('08', 'm');
266
+ max-width: calc(100% - 60px);
267
+ padding: 30px;
268
+ position: relative;
269
+
270
+ @include set-from-screen($responsive-breakpoint) {
271
+ margin: auto;
272
+ padding: $mu250 0 $mu250 0;
273
+ text-align: center;
274
+ }
275
+ }
276
+
277
+ &__input {
278
+ width: 100%;
279
+ }
280
+
281
+ &__form {
282
+ padding: 30px;
283
+ width: calc(100% - 60px);
284
+
285
+ @include set-from-screen($responsive-breakpoint) {
286
+ margin: auto;
287
+ padding: 0;
288
+ width: 400px;
289
+ }
290
+
291
+ &__checkbox {
292
+ align-items: center;
293
+ display: flex;
294
+ justify-content: center;
295
+ }
296
+
297
+ &__radio {
298
+ margin-right: 15px;
299
+ }
300
+
301
+ &__label {
302
+ @include set-font-face('regular');
303
+ @include set-font-scale('04', 's');
304
+ margin-bottom: $mu050;
305
+
306
+ span {
307
+ @include set-font-face('regular');
308
+ @include set-font-scale('03', 's');
309
+ color: $color-grey-500;
310
+ }
311
+ }
312
+
313
+ &__row {
314
+ margin-bottom: $mu050;
315
+
316
+ &.space {
317
+ margin-bottom: $mu150;
318
+ }
319
+ }
320
+
321
+ &__col {
322
+ width: 100%;
323
+
324
+ @include set-from-screen($responsive-breakpoint) {
325
+ width: 50%;
326
+ }
327
+
328
+ &:first-child {
329
+ padding-right: 15px;
330
+ }
331
+
332
+ &:last-child {
333
+ padding-left: 15px;
334
+ }
335
+ }
336
+ }
337
+
338
+ &__container--button {
339
+ margin: $mu250 0;
340
+
341
+ button {
342
+ &:first-child {
343
+ margin-right: 15px;
344
+ }
345
+
346
+ &:last-child {
347
+ margin-left: 15px;
348
+ }
349
+ }
350
+
351
+ &__ok {
352
+ margin-bottom: $mu100;
353
+ }
354
+ }
355
+ }
356
+ </style>
@@ -0,0 +1,17 @@
1
+ {
2
+ "viewModel": {
3
+ "backLabel": "Question précédente",
4
+ "label": "Renseignez notre formulaire pour être recontacté",
5
+ "typeLead": "HEAT_PUMP"
6
+ },
7
+ "callToActions": [
8
+ {
9
+ "type": "button",
10
+ "label": "Envoyer",
11
+ "action": {
12
+ "type": "submit"
13
+ },
14
+ "bordered": false
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,30 @@
1
+ import axios from 'axios';
2
+ import { escape } from '../../services/htmlEscape';
3
+
4
+ export const clientApi = axios.create({
5
+ baseURL: '/project-booster/api',
6
+ });
7
+
8
+ if ((<any>window).config) {
9
+ clientApi.defaults.baseURL = (<any>window).config.VUE_APP_DEFAULT_BASE_URL;
10
+ }
11
+
12
+ export const updateTrezorApiClient = (config: { apiKey: string; baseUrl: string; apiHeader: string }) => {
13
+ if (config.apiKey) {
14
+ const header = config.apiHeader ?? 'X-Gateway-ApiKey';
15
+ clientApi.defaults.headers.common[header] = config.apiKey;
16
+ }
17
+ if (config.baseUrl) {
18
+ clientApi.defaults.baseURL = config.baseUrl;
19
+ }
20
+ };
21
+
22
+ export const sendTrezorForm = async (typeLead: string, payload: object) => {
23
+ const response = await clientApi.post(`leads/${typeLead}`, payload);
24
+
25
+ return JSON.parse(
26
+ JSON.stringify(response.data).replace(/:"([^"]+)"/g, (match, $1) => {
27
+ return `: "${escape($1)}"`;
28
+ }),
29
+ );
30
+ };
@@ -0,0 +1,39 @@
1
+ import { ActionContext } from 'vuex';
2
+ import { State } from '@/stores/state';
3
+ import { sendTrezorForm } from '@/services/api/trezorApi';
4
+
5
+ export interface TrezorState {
6
+ // eslint-disable-next-line @typescript-eslint/ban-types
7
+ typeLead: string;
8
+ formData: object;
9
+ }
10
+
11
+ type TrezorContext = ActionContext<TrezorState, State>;
12
+
13
+ export default {
14
+ namespaced: true,
15
+ state: {
16
+ typeLead: '',
17
+ formData: '',
18
+ },
19
+ getters: {},
20
+ mutations: {
21
+ setTypeLead: (state: TrezorState, typeLead: string) => {
22
+ state.typeLead = typeLead;
23
+ },
24
+ setFormData: (state: TrezorState, formData: object) => {
25
+ state.formData = formData;
26
+ },
27
+ },
28
+ actions: {
29
+ sendFormData: async (
30
+ { commit, state }: TrezorContext,
31
+ { payload, typeLead }: { payload: object; typeLead: string },
32
+ ) => {
33
+ if (!typeLead) {
34
+ const sendData = await sendTrezorForm(typeLead, payload);
35
+ commit('setFormData', sendData);
36
+ }
37
+ },
38
+ },
39
+ };
@@ -9,6 +9,7 @@ import toolsStore from './modules/toolsStore';
9
9
  import documentsStore from './modules/documentsStore';
10
10
  import consentStore from './modules/consentStore';
11
11
  import productsStore from './modules/productsStore';
12
+ import trezorStore from './modules/trezorStore';
12
13
  import cloneDeep from 'lodash.clonedeep';
13
14
  import { State } from '@/stores/state';
14
15
  import { ActionContext } from 'vuex';
@@ -28,6 +29,7 @@ export default {
28
29
  documentsPictures: cloneDeep(documentsStore),
29
30
  consent: consentStore,
30
31
  products: productsStore,
32
+ trezor: trezorStore,
31
33
  },
32
34
  mutations: {
33
35
  eventBusSendEvent(state: State, { code, payload }: { code: string; payload: any }) {