project-booster-vue 10.0.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-booster-vue",
3
- "version": "10.0.0",
3
+ "version": "10.1.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -100,11 +100,13 @@ const props = defineProps({
100
100
  * onMounted load product and load this
101
101
  */
102
102
  onMounted(() => {
103
- store.dispatch('products/loadProduct', {
104
- payload: props.payload.viewModel.defaultProduct,
105
- storeId: metadata && metadata.storeId ? metadata.storeId : '',
106
- });
107
- product.value = store.getters['products/getCurrentProduct'];
103
+ if (store.hasModule('products/loadProduct')) {
104
+ store.dispatch('products/loadProduct', {
105
+ payload: props.payload.viewModel.defaultProduct,
106
+ storeId: metadata && metadata.storeId ? metadata.storeId : '',
107
+ });
108
+ product.value = store.getters['products/getCurrentProduct'];
109
+ }
108
110
  });
109
111
 
110
112
  /**
@@ -123,6 +123,26 @@
123
123
  />
124
124
  </m-flex>
125
125
  </m-flexy>
126
+
127
+ <!-- Help link/modal -->
128
+ <m-flex class="pb-question__help" v-if="payload.helpArea" direction="column">
129
+ <div v-for="helpItem in payload.helpArea" :key="helpItem.type">
130
+ <div v-if="helpItem.type === 'text'">
131
+ <p class="pb-question__help__text">{{ helpItem.label }}</p>
132
+ </div>
133
+ <div v-if="helpItem.type === 'link'">
134
+ <m-link
135
+ class="pb-question__help__link"
136
+ :label="helpItem.label"
137
+ width="full"
138
+ size="l"
139
+ theme="primary"
140
+ @click="handleHelpClick(helpItem.action)"
141
+ />
142
+ </div>
143
+ </div>
144
+ </m-flex>
145
+
126
146
  <m-notification class="pb-question__notifications" :type="notification.type" v-if="notification">
127
147
  <template #default>
128
148
  <strong style="font-size: 16px; line-height: 20px">{{ notification.title }}</strong>
@@ -180,24 +200,7 @@
180
200
  ></m-link>
181
201
  </div>
182
202
  </div>
183
- <div class="pb-question__padding-bottom" v-if="!payload.helpArea" />
184
- <m-flex class="pb-question__help" v-if="payload.helpArea" direction="column">
185
- <div v-for="helpItem in payload.helpArea" :key="helpItem.type">
186
- <div v-if="helpItem.type === 'text'">
187
- <p class="pb-question__help__text">{{ helpItem.label }}</p>
188
- </div>
189
- <div v-if="helpItem.type === 'link'">
190
- <m-link
191
- class="pb-question__help__link"
192
- :label="helpItem.label"
193
- width="full"
194
- size="l"
195
- theme="primary"
196
- @click="handleHelpClick(helpItem.action)"
197
- />
198
- </div>
199
- </div>
200
- </m-flex>
203
+
201
204
  <pb-sticky-footer
202
205
  ref="pbStickyFooter"
203
206
  class="pb-question__sticky-footer"
@@ -283,6 +286,7 @@
283
286
  :modalTitle="helpDialog?.viewModelDialog.headerTitle"
284
287
  :open="displayDialogHelp"
285
288
  @update:open="displayDialogHelp = false"
289
+ class="mc-modal--overflow"
286
290
  v-if="helpDialog"
287
291
  >
288
292
  <template #default>
@@ -1063,7 +1067,7 @@ $answers-apparition-duration: '0.5s';
1063
1067
  display: flex;
1064
1068
  flex-wrap: wrap;
1065
1069
  justify-content: flex-start;
1066
- margin: 0 auto $mu100;
1070
+ margin: 0 auto;
1067
1071
  max-width: 1024px;
1068
1072
  width: 100%;
1069
1073
 
@@ -1386,11 +1390,11 @@ $answers-apparition-duration: '0.5s';
1386
1390
  }
1387
1391
 
1388
1392
  &__help {
1389
- margin-bottom: $mu250;
1393
+ margin-bottom: $mu100;
1390
1394
 
1391
1395
  @include set-from-screen($responsive-breakpoint) {
1392
1396
  max-width: 554px;
1393
- margin: 0 auto;
1397
+ padding: $mu100;
1394
1398
  text-align: left;
1395
1399
  }
1396
1400
 
@@ -1411,6 +1415,11 @@ $answers-apparition-duration: '0.5s';
1411
1415
  width: calc(100% - 30px);
1412
1416
  margin: auto;
1413
1417
  margin-top: $mu200;
1418
+
1419
+ a,
1420
+ button {
1421
+ width: 100%;
1422
+ }
1414
1423
  }
1415
1424
 
1416
1425
  &__modal-link {
@@ -1456,6 +1465,7 @@ $responsive-breakpoint: 'm';
1456
1465
  background-repeat: no-repeat;
1457
1466
  background-size: 100% auto;
1458
1467
  padding: 120px 0;
1468
+ margin-bottom: 50px;
1459
1469
  }
1460
1470
  }
1461
1471
 
@@ -0,0 +1,245 @@
1
+ import { nestedAppDecorator } from '../../../.storybook/nested-app-decorator';
2
+ import { Anchor, Story, Preview, Meta, Props, ArgsTable, Source, Canvas } from '@storybook/addon-docs';
3
+ import PbScenario from './PbScenario';
4
+ import estimatesStore from '../../stores/modules/estimatesStore';
5
+ import DEFAULT_PAYLOAD from './scenarii/solar-panel.json';
6
+ import { TemplateSandbox } from './PbScenario.stories.mdx';
7
+ import simpleScenarioSchema from './Simple-scenario.svg';
8
+
9
+ <Meta
10
+ title="Project Booster/Scenario/PbScenario 🦠/Test (locally)/Solar Panel"
11
+ component={PbScenario}
12
+ argTypes={{
13
+ scenarios: {
14
+ table: {
15
+ defaultValue: {
16
+ summary: 'object',
17
+ detail: JSON.stringify(DEFAULT_PAYLOAD, null, ' '),
18
+ },
19
+ },
20
+ control: {
21
+ type: 'object',
22
+ },
23
+ },
24
+ onScenarioCompleted: {
25
+ action: 'Scenario completed',
26
+ table: { disable: true },
27
+ },
28
+ onScenarioEvent: {
29
+ action: 'Scenario event occured',
30
+ table: { disable: true },
31
+ },
32
+ }}
33
+ decorators={[
34
+ nestedAppDecorator(
35
+ {
36
+ modules: {
37
+ estimates: estimatesStore,
38
+ },
39
+ actions: {
40
+ sendEventToBus({}, payload) {
41
+ console.log('Event sent to bus', payload);
42
+ },
43
+ },
44
+ },
45
+ [{ path: '/steps/:stepCode/previous/:answers', component: PbScenario }],
46
+ ),
47
+ ]}
48
+ parameters={{
49
+ layout: 'fullscreen',
50
+ }}
51
+ />
52
+
53
+ <Anchor storyId="project-booster-scenario-pbscenario-features-conditional--101-simple-scenario" />
54
+
55
+ # Features - PbScenario
56
+
57
+ ## Scenarios and steps management
58
+
59
+ The scenario component allows to manage **scenarios** and **steps** :
60
+
61
+ - a **scenario** is a sequence of steps,
62
+ - a **step** is one interaction from the user perspective.
63
+
64
+ ## Simple scenario
65
+
66
+ The scenario component will use the bounded scenarios object containing all the scenarios and steps:
67
+
68
+ <details>
69
+ <summary class="css-1p8ieni">
70
+ Show/Hide <strong>scenario code</strong>:
71
+ </summary>
72
+ <Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD, null, ' ')} />
73
+ </details>
74
+
75
+ A scenario object is composed of a map of steps. A step can be a standard step or a nested scenario.
76
+
77
+ This simple scenario contains:
78
+
79
+ - the `__START__` scenario which is used by the component to know where to find the main scenario, its presence is mandatory,
80
+ - the `MAIN_SCENARIO` (name it as you want) definition, pointing to its first step `MAIN_SCENARIO--STEP_1`,
81
+ - the `MAIN_SCENARIO--STEP_1` (name it as you want) step with the component `PbCard` used for this step and the next
82
+ step `MAIN_SCENARIO--STEP_2` (name it as you want) to reach when completed,
83
+ - the `MAIN_SCENARIO--STEP_2` (name it as you want) step with the component `PbCard` used for this step and the next
84
+ step `__END__` to reach when completed,
85
+ - the `__END__` step with the component `PbCard` used for this step which indicates to the
86
+ component that the main scenario is completed, its presence is mandatory to address properly the nested scenario feature.
87
+
88
+ NB: **`__START__`** and **`__END__`** are mandatory step codes to allow the component to work properly, **DO NOT RENAME THEM**. The `__END__`
89
+ can display a component.
90
+
91
+ <img src={simpleScenarioSchema} />
92
+
93
+ <Canvas>
94
+ <Story
95
+ name="Solar Panel"
96
+ inline={false}
97
+ height="758px"
98
+ args={{ scenarios: DEFAULT_PAYLOAD, minHeight: 'auto' }}
99
+ parameters={{ storyshots: { disable: true } }}
100
+ >
101
+ {TemplateSandbox.bind({})}
102
+ </Story>
103
+ </Canvas>
104
+
105
+ ## Focus on scenario definition
106
+
107
+ Here is the scenario definition:
108
+
109
+ ```json
110
+ {
111
+ "SCENARIO_CODE": {
112
+ "code": "SCENARIO_CODE",
113
+ "type": "SCENARIO",
114
+ "stepCode": "FIRST_STEP_CODE",
115
+ "meta": {
116
+ "your-stuff": "here"
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ where:
123
+
124
+ | | |
125
+ | --------------- | -------------------------------------------------------------------------------------------------- |
126
+ | `SCENARIO_CODE` | the scenario code uniquely identifying it in the scenario map, |
127
+ | `code` | the same as above, the data is duplicated inside the object to simplify steps/scenarios management |
128
+ | `type` | the type of step, (could be `STEP`, `SCENARIO`) |
129
+ | `stepCode` | the first step of the scenario |
130
+ | `meta` | data needed by the scenario host |
131
+
132
+ ## Focus on step definition
133
+
134
+ ### Mandatory properties
135
+
136
+ Here is the smallest step definition (it can be extended with other properties):
137
+
138
+ ```json
139
+ {
140
+ "STEP_CODE": {
141
+ "code": "STEP_CODE",
142
+ "type": "STEP",
143
+ "component": "PbComponentForStepScreen",
144
+ "payload": {}
145
+ }
146
+ }
147
+ ```
148
+
149
+ | | |
150
+ | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
151
+ | `STEP_CODE` | the step code uniquely identifying it in the scenario map |
152
+ | `type` | the type of step, (could be `STEP` or `SCENARIO`) |
153
+ | `component` | the component that will be used to materialize the step on the screen. See _Step component_ section below for more information |
154
+ | `payload` | the payload containing specific business data for the step |
155
+
156
+ ### Step component
157
+
158
+ The step is associated with a component. This is the component which will be displayed for the step.
159
+ A `payload` property is also available to provide:
160
+
161
+ - the **business data** needed to address the business use case (ex: the answers of a question),
162
+ - the `viewModel` property which contains the component **view model** (ie. the data used to customise the component: label, images, ...).
163
+
164
+ The `payload` property is not mandatory as the component does not always need customisation data.
165
+
166
+ Here is a step definition with the component definition:
167
+
168
+ ```json
169
+ {
170
+ "STEP_CODE": {
171
+ "code": "STEP_CODE",
172
+ "type": "STEP",
173
+ "component": "PbComponentForStepScreen",
174
+ "payload": {}
175
+ }
176
+ }
177
+ ```
178
+
179
+ #### Step components API
180
+
181
+ The components API consists of :
182
+
183
+ - the `payload` provided by the `PbScenario` component which contains business data and view model,
184
+ - the `step-completed` event emitted by the component itself when he has finished its job.
185
+
186
+ ##### Payload bound property
187
+
188
+ The `payload` property bound to the component will contain all the data needed by the component to do its job.
189
+
190
+ This is not mandatory as the component may be self sufficient to execute its tasks.
191
+
192
+ ##### step-completed event
193
+
194
+ The `step-completed` event is raised when the component has finished its job. It contains:
195
+
196
+ - the `answers` property containing the result of the component job,
197
+ - the `nextStep` property containing a step to override the default `nextStep` defined on the current step.
198
+
199
+ ## Going to next step
200
+
201
+ After the user completes the task provided by the component, the component will emit the `step-completed` event.
202
+
203
+ The `step-completed` event payload will contain the `answers` resulting from the user interactions and an optional
204
+ `nextStep`:
205
+
206
+ - If a `nextStep` is provided by the component, the scenario will use this as a next step.
207
+ - If no `nextStep` is provided by the component, the scenario component will look for the `nextStep` property of
208
+ the current step,
209
+ - If no `nextStep` is provided/found, the current scenario will be considered as finished and the scenario component
210
+ will try to resume the parent scenario.
211
+
212
+ The `nextStep` object only contains a `code` property valued with the code of the targeted next step.
213
+ If the code is `null`, it will react as if the `nextStep` is not provided (the current scenario will be considered
214
+ as finished and the scenario component will try to resume the parent scenario, if there is no scenario to resume,
215
+ the scenario will go to the `__END__`).
216
+
217
+ Next step example to navigate to a step or a nested scenario:
218
+
219
+ ```json
220
+ {
221
+ "nextStep": {
222
+ "code": "NEXT_STEP_OR_SCENARIO_CODE"
223
+ }
224
+ }
225
+ ```
226
+
227
+ Next step example to navigate to finish the current (nested) scenario:
228
+
229
+ ```json
230
+ {
231
+ "nextStep": {
232
+ "code": null
233
+ }
234
+ }
235
+ ```
236
+
237
+ Next step example to navigate to end the main scenario:
238
+
239
+ ```json
240
+ {
241
+ "nextStep": {
242
+ "code": "__END__"
243
+ }
244
+ }
245
+ ```
@@ -25,6 +25,9 @@
25
25
  "code": "LMFR_QUESTION_WORKS",
26
26
  "type": "STEP",
27
27
  "component": "MPbQuestion",
28
+ "analytics": {
29
+ "funnel_name": "SDB_step1"
30
+ },
28
31
  "slots": {
29
32
  "stickyBottom": {
30
33
  "display": false
@@ -280,6 +283,9 @@
280
283
  "code": "LMFR_BATHROOM_BATHTUB_TYPE",
281
284
  "type": "STEP",
282
285
  "component": "MPbQuestion",
286
+ "analytics": {
287
+ "funnel_name": "SDB_step2"
288
+ },
283
289
  "slots": {
284
290
  "beforeContent": {
285
291
  "display": true,
@@ -387,6 +393,9 @@
387
393
  "code": "LMFR_BATHROOM_SHOWER_TYPE",
388
394
  "type": "STEP",
389
395
  "component": "MPbQuestion",
396
+ "analytics": {
397
+ "funnel_name": "SDB_step3"
398
+ },
390
399
  "slots": {
391
400
  "beforeContent": {
392
401
  "display": true,
@@ -476,6 +485,9 @@
476
485
  "code": "LMFR_BATHROOM_FURNITURE_TYPE",
477
486
  "type": "STEP",
478
487
  "component": "MPbQuestion",
488
+ "analytics": {
489
+ "funnel_name": "SDB_step4_1/4"
490
+ },
479
491
  "slots": {
480
492
  "beforeContent": {
481
493
  "display": true,
@@ -579,6 +591,9 @@
579
591
  "code": "LMFR_BATHROOM_WC_TYPE",
580
592
  "type": "STEP",
581
593
  "component": "MPbQuestion",
594
+ "analytics": {
595
+ "funnel_name": "SDB_step5"
596
+ },
582
597
  "slots": {
583
598
  "beforeContent": {
584
599
  "display": true,
@@ -629,6 +644,9 @@
629
644
  "code": "LMFR_BATHROOM_STYLE",
630
645
  "type": "STEP",
631
646
  "component": "MPbQuestion",
647
+ "analytics": {
648
+ "funnel_name": "SDB_step3"
649
+ },
632
650
  "slots": {
633
651
  "beforeContent": {
634
652
  "display": true,
@@ -756,6 +774,21 @@
756
774
  }
757
775
  }
758
776
  },
777
+ "callToActions": [
778
+ {
779
+ "label": "Précédent",
780
+ "theme": "bordered-primary-02",
781
+ "nextStep": {
782
+ "code": "__BACK__"
783
+ }
784
+ },
785
+ {
786
+ "label": "Suivant",
787
+ "nextStep": {
788
+ "code": "LMFR_QUESTION_ADD_PICTURE"
789
+ }
790
+ }
791
+ ],
759
792
  "skippable": [
760
793
  {
761
794
  "isAnswer": true,
@@ -784,6 +817,9 @@
784
817
  "code": "LMFR_QUESTION_PLAN_INFO",
785
818
  "type": "STEP",
786
819
  "component": "MPbQuestion",
820
+ "analytics": {
821
+ "funnel_name": "SDB_step4_3/4"
822
+ },
787
823
  "slots": {
788
824
  "beforeContent": {
789
825
  "display": true,
@@ -861,6 +897,9 @@
861
897
  "code": "LMFR_QUESTION_ADD_PLAN_LATER",
862
898
  "type": "STEP",
863
899
  "component": "MPbAlert",
900
+ "analytics": {
901
+ "funnel_name": "SDB_step4_2/4"
902
+ },
864
903
  "slots": {
865
904
  "beforeContent": {
866
905
  "display": true,
@@ -935,6 +974,9 @@
935
974
  "code": "LMFR_QUESTION_ADD_PICTURE",
936
975
  "type": "STEP",
937
976
  "component": "MPbUploadDocument",
977
+ "analytics": {
978
+ "funnel_name": "SDB_step5_1/2"
979
+ },
938
980
  "slots": {
939
981
  "beforeContent": {
940
982
  "display": true,
@@ -1021,6 +1063,9 @@
1021
1063
  "code": "LMFR_QUESTION_ADD_PLAN",
1022
1064
  "type": "STEP",
1023
1065
  "component": "MPbUploadDocument",
1066
+ "analytics": {
1067
+ "funnel_name": "SDB_step4_4/4"
1068
+ },
1024
1069
  "slots": {
1025
1070
  "beforeContent": {
1026
1071
  "display": true,
@@ -1080,16 +1125,13 @@
1080
1125
  "actions": {
1081
1126
  "BACK": {
1082
1127
  "label": "Précédent",
1083
- "theme": "bordered-neutral",
1084
- "icon": "ArrowBack48",
1128
+ "theme": "bordered-primary-02",
1085
1129
  "nextStep": {
1086
1130
  "code": "__BACK__"
1087
1131
  }
1088
1132
  },
1089
1133
  "VALIDATE": {
1090
1134
  "label": "Suivant",
1091
- "icon": "ArrowNext48",
1092
- "iconPosition": "right",
1093
1135
  "nextStep": {
1094
1136
  "code": "LMFR_QUESTION_ADD_PICTURE"
1095
1137
  }
@@ -1111,6 +1153,9 @@
1111
1153
  "code": "LMFR_QUESTION_WORK_START_DATE",
1112
1154
  "type": "STEP",
1113
1155
  "component": "MPbQuestion",
1156
+ "analytics": {
1157
+ "funnel_name": "SDB_step6"
1158
+ },
1114
1159
  "slots": {
1115
1160
  "beforeContent": {
1116
1161
  "display": true,
@@ -1186,6 +1231,9 @@
1186
1231
  "code": "LMFR_QUESTION_INSTALLATION",
1187
1232
  "type": "STEP",
1188
1233
  "component": "MPbQuestion",
1234
+ "analytics": {
1235
+ "funnel_name": "SDB_step7"
1236
+ },
1189
1237
  "slots": {
1190
1238
  "beforeContent": {
1191
1239
  "display": true,
@@ -1266,6 +1314,9 @@
1266
1314
  "code": "LMFR_QUESTION_BUDGET",
1267
1315
  "type": "STEP",
1268
1316
  "component": "MPbQuestion",
1317
+ "analytics": {
1318
+ "funnel_name": "SDB_step8"
1319
+ },
1269
1320
  "slots": {
1270
1321
  "beforeContent": {
1271
1322
  "display": true,
@@ -1359,6 +1410,9 @@
1359
1410
  "LMFR_COMMENT": {
1360
1411
  "code": "LMFR_COMMENT",
1361
1412
  "type": "STEP",
1413
+ "analytics": {
1414
+ "funnel_name": "SDB_step9"
1415
+ },
1362
1416
  "component": "MPbExitOptions",
1363
1417
  "slots": {
1364
1418
  "beforeContent": {
@@ -1400,6 +1454,9 @@
1400
1454
  "code": "BATHROOM_END",
1401
1455
  "type": "STEP",
1402
1456
  "component": "PbPedagogy",
1457
+ "analytics": {
1458
+ "funnel_name": "SDB_step5_2/2"
1459
+ },
1403
1460
  "payload": {
1404
1461
  "viewModel": {
1405
1462
  "title": "Nous avons bien pris rendez-vous et enregistré vos informations",
@@ -0,0 +1,644 @@
1
+ {
2
+ "__START__": {
3
+ "code": "__START__",
4
+ "type": "SCENARIO",
5
+ "meta": {
6
+ "estimatorId": "c353f7f8-d405-4000-86a0-63e5053a769e",
7
+ "businessUnit": "001",
8
+ "projectName": "Estimer le coût de vos panneaux photovoltaïques",
9
+ "webAnalytics": {
10
+ "category": "Panneaux photovoltaïques",
11
+ "scenario": "Estimation-budget-panneaux-photovoltaiques"
12
+ }
13
+ },
14
+ "stepCode": "SOLAR_PANEL"
15
+ },
16
+ "SOLAR_PANEL": {
17
+ "code": "SOLAR_PANEL",
18
+ "type": "SCENARIO",
19
+ "stepCode": "LMFR_SOLAR_PANEL_DISPLAY_PRODUCT"
20
+ },
21
+
22
+ "LMFR_SOLAR_PANEL_DISPLAY_PRODUCT": {
23
+ "code": "LMFR_SOLAR_PANEL_DISPLAY_PRODUCT",
24
+ "type": "STEP",
25
+ "component": "MPbProducts",
26
+ "payload": {
27
+ "viewModel": {
28
+ "label": "Estimer vos aides pour votre projet",
29
+ "defaultProduct": {
30
+ "designation": "Installation de panneaux photovoltaïques"
31
+ }
32
+ },
33
+ "callToActions": [
34
+ {
35
+ "type": "button",
36
+ "label": "Commencer",
37
+ "action": {
38
+ "type": "STEP",
39
+ "code": "LMFR_SOLAR_PANEL_QUESTION_HOUSING_TYPE"
40
+ },
41
+ "bordered": false
42
+ }
43
+ ]
44
+ }
45
+ },
46
+ "LMFR_SOLAR_PANEL_QUESTION_HOUSING_TYPE": {
47
+ "code": "LMFR_SOLAR_PANEL_QUESTION_HOUSING_TYPE",
48
+ "type": "STEP",
49
+ "component": "MPbQuestion",
50
+ "payload": {
51
+ "viewModel": {
52
+ "label": "Dans quel type de logement habitez-vous ?",
53
+ "answersComponent": "MPbCard",
54
+ "progressBar": true,
55
+ "widthFromL": "1of3",
56
+ "forceOneCardPerLineOnMobile": true
57
+ },
58
+ "answers": {
59
+ "HOUSE": {
60
+ "code": "HOUSE",
61
+ "viewModel": {
62
+ "title": "Une maison",
63
+ "image": "https://storage.googleapis.com/project-booster-media/energyrenovation/house.png"
64
+ }
65
+ },
66
+ "APARTMENT": {
67
+ "code": "APARTMENT",
68
+ "viewModel": {
69
+ "title": "Un appartement",
70
+ "image": "https://storage.googleapis.com/project-booster-media/energyrenovation/apartement.jpg"
71
+ },
72
+ "notification": {
73
+ "type": "warning",
74
+ "title": "Nous attirons votre attention !",
75
+ "content": "Nous ne realisons pas de projets de panneau photovoltaïque dans les appartements dans la mesure où votre toit est partagé",
76
+ "buttonText": "Voir d'autres solutions pour faire des économies de chauffage",
77
+ "buttonHref": "https://www.leroymerlin.fr/economie-energie/"
78
+ }
79
+ }
80
+ }
81
+ },
82
+ "nextStep": {
83
+ "conditionals": [
84
+ {
85
+ "conditions": ["isAnswerMatching('LMFR_SOLAR_PANEL_QUESTION_HOUSING_TYPE', 'HOUSE')"],
86
+ "nextStep": {
87
+ "code": "LMFR_SOLAR_PANEL_QUESTION_LOCATION"
88
+ }
89
+ }
90
+ ]
91
+ },
92
+ "slots": {
93
+ "beforeContent": {
94
+ "display": true,
95
+ "sizeXlWidth": true,
96
+ "component": "MPbProgress",
97
+ "totalStep": 5,
98
+ "currentStep": 1
99
+ }
100
+ }
101
+ },
102
+ "LMFR_SOLAR_PANEL_QUESTION_LOCATION": {
103
+ "code": "LMFR_SOLAR_PANEL_QUESTION_LOCATION",
104
+ "type": "STEP",
105
+ "component": "MPbCitySearch",
106
+ "payload": {
107
+ "value": {
108
+ "city": {
109
+ "inseeCode": "${getAnswerValue('LMFR_SOLAR_PANEL_QUESTION_LOCATION', 'city.inseeCode')}",
110
+ "postalCode": "${getAnswerValue('LMFR_SOLAR_PANEL_QUESTION_LOCATION', 'city.postalCode')}",
111
+ "name": "${getAnswerValue('LMFR_SOLAR_PANEL_QUESTION_LOCATION', 'city.name')}",
112
+ "region": "${getAnswerValue('LMFR_SOLAR_PANEL_QUESTION_LOCATION', 'city.region')}"
113
+ }
114
+ },
115
+ "viewModel": {
116
+ "backLabel": "Question précédente",
117
+ "label": "Dans quelle ville aura lieu les travaux ?",
118
+ "placeholder": "Code postal, Ville",
119
+ "actionLabel": "Continuer"
120
+ }
121
+ },
122
+ "nextStep": {
123
+ "code": "LMFR_SOLAR_PANEL_QUESTION_LINKY"
124
+ },
125
+ "slots": {
126
+ "beforeContent": {
127
+ "display": true,
128
+ "component": "MPbProgress",
129
+ "totalStep": 5,
130
+ "currentStep": 2
131
+ }
132
+ }
133
+ },
134
+ "LMFR_SOLAR_PANEL_QUESTION_LINKY": {
135
+ "code": "LMFR_SOLAR_PANEL_QUESTION_LINKY",
136
+ "type": "STEP",
137
+ "component": "MPbQuestion",
138
+ "payload": {
139
+ "viewModel": {
140
+ "label": "Votre logement est-il équipé d’un compteur Linky ?",
141
+ "answersComponent": "MPbCard",
142
+ "progressBar": true,
143
+ "widthFromL": "1of3",
144
+ "forceOneCardPerLineOnMobile": true
145
+ },
146
+ "answers": {
147
+ "YES": {
148
+ "code": "YES",
149
+ "viewModel": {
150
+ "title": "Oui"
151
+ }
152
+ },
153
+ "NO": {
154
+ "code": "NO",
155
+ "viewModel": {
156
+ "title": "Non"
157
+ },
158
+ "notification": {
159
+ "type": "warning",
160
+ "content": "Le compteur électrique Linky vous permet d'être facturé selon votre consommation électrique réelle. Sans ce compteur nous ne pouvons pas installer de panneaux solaires. Pour l’installer, nous vous invitons à vous rapprocher d'Enedis",
161
+ "buttonText": "Voir d'autres solutions pour faire des économies de chauffage",
162
+ "buttonHref": "https://www.leroymerlin.fr/economie-energie/"
163
+ }
164
+ }
165
+ },
166
+ "helpArea": [
167
+ {
168
+ "type": "link",
169
+ "label": "À quoi ressemble un compteur Linky ?",
170
+ "action": {
171
+ "type": "MODAL",
172
+ "viewModelDialog": {
173
+ "headerTitle": "À quoi ressemble un compteur Linky ?",
174
+ "htmlContent": "<b style='font-size:18px;'>Le compteur Linky ressemble à un boîtier rectangulaire en plastique. Il a un écran qui affiche la consommation d'électricité en kilowattheures (kWh)</b><br/><br/><p>C’est un compteur d'électricité intelligent installé par Enedis en France. Il permet de mesurer la consommation d'électricité en temps réel et de transmettre les données sans intervention humaine grâce à une communication sans fil.</p><br/>",
175
+ "imgUrl": "https://storage.googleapis.com/project-booster-media/energyrenovation/linky.png"
176
+ }
177
+ }
178
+ }
179
+ ]
180
+ },
181
+ "nextStep": {
182
+ "conditionals": [
183
+ {
184
+ "conditions": ["isAnswerMatching('LMFR_SOLAR_PANEL_QUESTION_LINKY', 'YES')"],
185
+ "nextStep": {
186
+ "code": "LMFR_SOLAR_PANEL_QUESTION_EXPOSED_ROOF_AREA"
187
+ }
188
+ }
189
+ ]
190
+ },
191
+ "slots": {
192
+ "beforeContent": {
193
+ "display": true,
194
+ "sizeXlWidth": true,
195
+ "component": "MPbProgress",
196
+ "totalStep": 5,
197
+ "currentStep": 3
198
+ }
199
+ }
200
+ },
201
+ "LMFR_SOLAR_PANEL_QUESTION_EXPOSED_ROOF_AREA": {
202
+ "code": "LMFR_SOLAR_PANEL_QUESTION_EXPOSED_ROOF_AREA",
203
+ "type": "STEP",
204
+ "component": "MPbQuestion",
205
+ "payload": {
206
+ "viewModel": {
207
+ "label": "Quelle surface de votre toiture est exposée au sud, ouest et/ou est ?",
208
+ "answersComponent": "MPbCard",
209
+ "progressBar": true,
210
+ "widthFromL": "1of3",
211
+ "forceOneCardPerLineOnMobile": true,
212
+ "notification": {
213
+ "type": "warning",
214
+ "title": "Attention : ne prenez pas en compte toutes les parties de la toiture exposées au nord",
215
+ "content": "Ceci comprend aussi les parties exposées au nord-est et nord-ouest. "
216
+ }
217
+ },
218
+ "answers": {
219
+ "LESS_THAN_20": {
220
+ "code": "LESS_THAN_20",
221
+ "viewModel": {
222
+ "title": "Moins de 20 m²"
223
+ }
224
+ },
225
+ "BETWEEN_20_AND_35": {
226
+ "code": "BETWEEN_20_AND_35",
227
+ "viewModel": {
228
+ "title": "20 à 35 m²"
229
+ }
230
+ },
231
+ "MORE_THAN_35": {
232
+ "code": "MORE_THAN_35",
233
+ "viewModel": {
234
+ "title": "Plus de 35 m²"
235
+ }
236
+ },
237
+ "DONT_KNOW": {
238
+ "code": "DONT_KNOW",
239
+ "viewModel": {
240
+ "title": "Je ne sais pas"
241
+ }
242
+ }
243
+ },
244
+ "helpArea": [
245
+ {
246
+ "type": "link",
247
+ "label": "Comment calculer cette surface de toiture ?",
248
+ "action": {
249
+ "type": "MODAL",
250
+ "viewModelDialog": {
251
+ "headerTitle": "Comment calculer cette surface de toiture ?",
252
+ "htmlContent": "<b style='font-size:18px;'>Il est obligatoire de positionner ses panneaux solaires sur une toiture exposée au sud, est ou ouest.</b><br/><br/><p> Pour calculer la surface de votre toiture exposée au sud, nous vous conseillons d’utiliser <u>Geoportail</u>. Dans les outils cartographiques : trouvez l’onglet “Mesures” et l’outil “Mesurer une surface” pour tracer la surface de votre toiture éligible.</p><br/>",
253
+ "imgUrl": "https://storage.googleapis.com/project-booster-media/energyrenovation/roof_area_exposure.png",
254
+ "linkText": "Utiliser Geoportail",
255
+ "hrefLink": "https://www.geoportail.gouv.fr/"
256
+ }
257
+ }
258
+ }
259
+ ]
260
+ },
261
+ "nextStep": {
262
+ "code": "LMFR_SOLAR_PANEL_QUESTION_ELECTRICITY_COMSUMPTION"
263
+ },
264
+ "slots": {
265
+ "beforeContent": {
266
+ "display": true,
267
+ "component": "MPbProgress",
268
+ "sizeXlWidth": true,
269
+ "totalStep": 5,
270
+ "currentStep": 4
271
+ }
272
+ }
273
+ },
274
+ "LMFR_SOLAR_PANEL_QUESTION_ELECTRICITY_COMSUMPTION": {
275
+ "code": "LMFR_SOLAR_PANEL_QUESTION_ELECTRICITY_COMSUMPTION",
276
+ "type": "STEP",
277
+ "component": "MPbQuestion",
278
+ "payload": {
279
+ "viewModel": {
280
+ "label": "À combien s’élève votre consommation électrique annuelle ?",
281
+ "answersComponent": "MPbCard",
282
+ "progressBar": true,
283
+ "widthFromL": "1of3",
284
+ "forceOneCardPerLineOnMobile": true,
285
+ "notification": {
286
+ "type": "info",
287
+ "title": "Trouvez facilement cette information :",
288
+ "content": "<ul style='list-style:none; padding:0; margin:0;'><li>- En ligne via un compte client en ligne ou une application mobile.</li><li>- Sur votre facture d’électricité.</li></ul>"
289
+ }
290
+ },
291
+ "answers": {
292
+ "LESS_THAN_2000": {
293
+ "code": "LESS_THAN_2000",
294
+ "viewModel": {
295
+ "title": "Moins de 2 000 kWh"
296
+ }
297
+ },
298
+ "BETWEEN_2000_AND_4000": {
299
+ "code": "BETWEEN_2000_AND_4000",
300
+ "viewModel": {
301
+ "title": "2 000 à 4 000 kWh"
302
+ }
303
+ },
304
+ "BETWEEN_4000_AND_8000": {
305
+ "code": "BETWEEN_4000_AND_8000",
306
+ "viewModel": {
307
+ "title": "4 000 à 8 000 kWh"
308
+ }
309
+ },
310
+ "MORE_THAN_8000": {
311
+ "code": "MORE_THAN_8000",
312
+ "viewModel": {
313
+ "title": "Plus de 8 000 kWh"
314
+ }
315
+ },
316
+ "DONT_KNOW": {
317
+ "code": "DONT_KNOW",
318
+ "viewModel": {
319
+ "title": "Je ne sais pas"
320
+ }
321
+ }
322
+ }
323
+ },
324
+ "nextStep": {
325
+ "code": "SUMMARY"
326
+ },
327
+ "slots": {
328
+ "beforeContent": {
329
+ "display": true,
330
+ "component": "MPbProgress",
331
+ "sizeXlWidth": true,
332
+ "totalStep": 5,
333
+ "currentStep": 5
334
+ }
335
+ }
336
+ },
337
+
338
+ "SUMMARY": {
339
+ "code": "SUMMARY",
340
+ "type": "STEP",
341
+ "component": "MPbRestitutionList",
342
+ "nextStep": {
343
+ "type": "STEP",
344
+ "code": "__END__"
345
+ },
346
+ "slots": {
347
+ "stickyBottom": {
348
+ "display": false
349
+ }
350
+ },
351
+ "payload": {
352
+ "backLabel": {
353
+ "label": "Recommencer l'estimation",
354
+ "action": {
355
+ "type": "STEP",
356
+ "code": "SOLAR_PANEL"
357
+ }
358
+ },
359
+ "viewModel": {
360
+ "title": "Résultat de votre estimation",
361
+ "legalMentions": [
362
+ "* Montant estimatif établi selon les réponses renseignées et susceptible d'être ajusté lors du devis définitif. Ce montant n’engage pas Leroy Merlin."
363
+ ]
364
+ },
365
+ "callToActions": [
366
+ {
367
+ "type": "button",
368
+ "label": "Être rappelé par un expert",
369
+ "action": {
370
+ "type": "STEP",
371
+ "code": "LMFR_SOLAR_PANEL_QUESTION_CONTACT"
372
+ }
373
+ },
374
+ {
375
+ "conditions": ["runtimeOptions.projectMode === true"],
376
+ "type": "button",
377
+ "label": "J'enregistre mon estimation",
378
+ "bordered": true,
379
+ "action": {
380
+ "type": "STEP",
381
+ "code": "ESTIMATION_NAME"
382
+ }
383
+ },
384
+ {
385
+ "conditions": ["!runtimeOptions.projectMode && !runtimeOptions.estimateCreated"],
386
+ "type": "button",
387
+ "label": "J'enregistre mon estimation",
388
+ "bordered": true,
389
+ "action": {
390
+ "type": "MODAL",
391
+ "code": "SUMMARY",
392
+ "component": "PbProjectItemSave",
393
+ "payload": {
394
+ "link": "/projets/estimations/confirmation-de-votre-estimation-panneaux-photovoltaiques.html",
395
+ "viewModel": {
396
+ "type": "estimation",
397
+ "modalTitle": "Enregistrer l'estimation",
398
+ "roomsTitle": "Choisissez une pièce",
399
+ "projectList": {
400
+ "title": "Mes projets",
401
+ "buttonLabelSaveInNewProject": "Enregistrer dans un nouveau projet"
402
+ },
403
+ "titleErrorProject": "Le nom du projet doit avoir moins de 60 caractères",
404
+ "titleErrorType": "Le nom de l'estimation doit avoir moins de 60 caractères",
405
+ "notification": {
406
+ "success": {
407
+ "title": "Estimation enregistrée",
408
+ "text": "L’estimation",
409
+ "link": "Voir dans le projet"
410
+ },
411
+ "error": {
412
+ "title": "L’estimation n’a pas pu être enregistrée",
413
+ "text": "Une erreur est survenue"
414
+ }
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+ ]
421
+ }
422
+ },
423
+ "LMFR_SOLAR_PANEL_QUESTION_CONTACT": {
424
+ "code": "LMFR_SOLAR_PANEL_QUESTION_CONTACT",
425
+ "type": "STEP",
426
+ "component": "MPbQuestion",
427
+ "payload": {
428
+ "viewModel": {
429
+ "label": "Vous souhaitez être rappelé pour :",
430
+ "answersComponent": "MPbCard",
431
+ "forceOneCardPerLineOnMobile": true,
432
+ "widthFromL": "1of2",
433
+ "progressBar": true
434
+ },
435
+ "answers": {
436
+ "PHONE_CALL": {
437
+ "code": "PHONE_CALL",
438
+ "viewModel": {
439
+ "title": "Concrétiser mon projet d'isolation des planchers bas et profiter des aides",
440
+ "text": "Notre partenaire prend directement contact avec vous",
441
+ "image": "https://storage.googleapis.com/project-booster-media/energyrenovation/phone_call.jpg"
442
+ }
443
+ },
444
+ "ADVISOR_APPOINTEMENT": {
445
+ "code": "ADVISOR_APPOINTEMENT",
446
+ "viewModel": {
447
+ "title": "Etudier mon projet de rénovation énergétique global",
448
+ "text": "Notre expert en rénovation énergétique vous conseille",
449
+ "image": "https://storage.googleapis.com/project-booster-media/energyrenovation/advisor_appointement.png"
450
+ }
451
+ }
452
+ }
453
+ },
454
+ "nextStep": {
455
+ "conditionals": [
456
+ {
457
+ "conditions": ["isAnswerMatching('LMFR_SOLAR_PANEL_QUESTION_CONTACT', 'PHONE_CALL')"],
458
+ "nextStep": {
459
+ "code": "LMFR_SOLAR_PANEL_LEAD_FORM"
460
+ }
461
+ },
462
+ {
463
+ "conditions": ["isAnswerMatching('LMFR_SOLAR_PANEL_QUESTION_CONTACT', 'ADVISOR_APPOINTEMENT')"],
464
+ "nextStep": {
465
+ "code": "APPOINTMENT_FORM"
466
+ }
467
+ }
468
+ ]
469
+ },
470
+ "slots": {
471
+ "beforeContent": {
472
+ "display": true,
473
+ "component": "MPbProgress",
474
+ "totalStep": 5,
475
+ "currentStep": 2
476
+ }
477
+ }
478
+ },
479
+ "ESTIMATION_NAME": {
480
+ "code": "ESTIMATION_NAME",
481
+ "type": "STEP",
482
+ "nextStep": {
483
+ "code": "__END__"
484
+ },
485
+ "slots": {
486
+ "stickyBottom": {
487
+ "display": false
488
+ },
489
+ "beforeContent": {
490
+ "display": true,
491
+ "component": "MPbProgress",
492
+ "totalStep": 5,
493
+ "currentStep": 1
494
+ }
495
+ },
496
+ "component": "MPbNameInput",
497
+ "payload": {
498
+ "viewModel": {
499
+ "label": "Nommez votre estimation",
500
+ "backLabel": "Revoir l'estimation",
501
+ "placeholder": "Nom de l'estimation",
502
+ "actionLabel": "Sauvegarder mon estimation",
503
+ "validation": {
504
+ "maxLength": 60,
505
+ "maxErrorMessage": "Le nom de l'estimation doit avoir moins de 60 caractères",
506
+ "requiredErrorMessage": "Le nom de l'estimation est obligatoire."
507
+ }
508
+ },
509
+ "value": {
510
+ "projectName": "Estimation isolation des combles"
511
+ }
512
+ }
513
+ },
514
+ "APPOINTMENT_FORM": {
515
+ "code": "APPOINTMENT_FORM",
516
+ "type": "STEP",
517
+ "component": "PbAppointmentForm",
518
+ "payload": {
519
+ "viewModel": {
520
+ "calendarUrlPath": "leroy-merlin-renovation-energetique"
521
+ }
522
+ }
523
+ },
524
+ "LMFR_SOLAR_PANEL_LEAD_FORM": {
525
+ "code": "LMFR_SOLAR_PANEL_LEAD_FORM",
526
+ "type": "STEP",
527
+ "component": "PbTrezor",
528
+ "payload": {
529
+ "viewModel": {
530
+ "backLabel": "Question précédente",
531
+ "label": "Renseignez notre formulaire pour être recontacté",
532
+ "typeLead": "SOLAR_PANEL",
533
+ "defaultValue": {
534
+ "zipcode": {
535
+ "answerCode": "LMFR_SOLAR_PANEL_QUESTION_LOCATION",
536
+ "path": "city.postalCode"
537
+ }
538
+ },
539
+ "metadata": [
540
+ {
541
+ "attribut": "productRef",
542
+ "type": "string",
543
+ "answerCode": "LMFR_SOLAR_PANEL_DISPLAY_PRODUCT",
544
+ "path": "product.code"
545
+ }
546
+ ]
547
+ },
548
+ "callToActions": [
549
+ {
550
+ "type": "button",
551
+ "label": "Envoyer",
552
+ "action": {
553
+ "type": "STEP",
554
+ "conditionals": [
555
+ {
556
+ "conditions": ["isAnswerValue('LMFR_SOLAR_PANEL_LEAD_FORM', 'status', 'LEAD_CREATED_SUCCESSFULLY')"],
557
+ "nextStep": {
558
+ "code": "LMFR_SOLAR_PANEL_LEAD_CREATED"
559
+ }
560
+ },
561
+ {
562
+ "conditions": ["true"],
563
+ "nextStep": {
564
+ "code": "LMFR_SOLAR_PANEL_LEAD_CREATION_FAILED"
565
+ }
566
+ }
567
+ ]
568
+ },
569
+ "bordered": false
570
+ }
571
+ ]
572
+ }
573
+ },
574
+ "LMFR_SOLAR_PANEL_LEAD_CREATED": {
575
+ "code": "LMFR_SOLAR_PANEL_LEAD_CREATED",
576
+ "type": "STEP",
577
+ "component": "PbPedagogy",
578
+ "payload": {
579
+ "viewModel": {
580
+ "title": "Merci, votre demande a été prise en compte",
581
+ "icon": "https://storage.googleapis.com/project-booster-media/vad/fin-de-parcours/check_80.svg",
582
+ "description": [
583
+ {
584
+ "label": "Vous serez recontacté sous 48h par un conseiller",
585
+ "icon": "https://storage.googleapis.com/project-booster-media/vad/fin-de-parcours/picto_plan_pour-espace-projet-compte.png"
586
+ }
587
+ ]
588
+ },
589
+ "callToActions": [
590
+ {
591
+ "isAnswer": false,
592
+ "label": "Retourner à l’estimation",
593
+ "nextStep": {
594
+ "code": "SUMMARY"
595
+ }
596
+ }
597
+ ]
598
+ }
599
+ },
600
+ "LMFR_SOLAR_PANEL_LEAD_CREATION_FAILED": {
601
+ "code": "LMFR_SOLAR_PANEL_LEAD_CREATION_FAILED",
602
+ "type": "STEP",
603
+ "component": "PbPedagogy",
604
+ "payload": {
605
+ "viewModel": {
606
+ "title": "Désolé, votre région n'est pas prise en charge pour le moment",
607
+ "description": [
608
+ {
609
+ "label": "Veuillez vous rendre dans votre magasin Leroy Merlin le plus proche",
610
+ "icon": "https://storage.googleapis.com/project-booster-media/vad/fin-de-parcours/picto_plan_pour-espace-projet-compte.png"
611
+ }
612
+ ]
613
+ },
614
+ "callToActions": [
615
+ {
616
+ "isAnswer": false,
617
+ "label": "Retourner à l’estimation",
618
+ "bordered": false,
619
+ "nextStep": {
620
+ "code": "SUMMARY"
621
+ }
622
+ },
623
+ {
624
+ "isAnswer": false,
625
+ "label": "Quitter l'estimation",
626
+ "bordered": true,
627
+ "nextStep": {
628
+ "code": "LMFR_SOLAR_PANEL_ADVICE"
629
+ }
630
+ }
631
+ ]
632
+ }
633
+ },
634
+ "__END__": {
635
+ "code": "__END__",
636
+ "type": "END",
637
+ "component": "MPbQuestion",
638
+ "payload": {
639
+ "viewModel": {
640
+ "label": "End"
641
+ }
642
+ }
643
+ }
644
+ }
@@ -305,6 +305,12 @@ export default defineComponent({
305
305
  props.payload.viewModel.defaultValue?.zipcode?.path,
306
306
  ) || null;
307
307
 
308
+ const city =
309
+ getAnswerValue(
310
+ props.payload.viewModel.defaultValue?.city?.answerCode,
311
+ props.payload.viewModel.defaultValue?.city?.path,
312
+ ) || null;
313
+
308
314
  const convertToTypeOf = (typedVar: string, input: any): string | number | null => {
309
315
  if (!input) {
310
316
  return null;
@@ -338,7 +344,7 @@ export default defineComponent({
338
344
  address: '',
339
345
  additionalAddress: '',
340
346
  zipcode: zipcode,
341
- city: '',
347
+ city: city,
342
348
  email: '',
343
349
  phone: '',
344
350
  optin: false,
@@ -7,6 +7,10 @@
7
7
  "zipcode": {
8
8
  "answerCode": "LMFR_PREVIOUS_QUESTION",
9
9
  "path": "value"
10
+ },
11
+ "city": {
12
+ "answerCode": "LMFR_PREVIOUS_QUESTION",
13
+ "path": "value"
10
14
  }
11
15
  },
12
16
  "metadata": [