project-booster-vue 9.40.0 → 9.41.1

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.40.0",
3
+ "version": "9.41.1",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -352,7 +352,6 @@
352
352
 
353
353
  <script lang="ts">
354
354
  import { defineComponent, PropType } from 'vue';
355
- import objectPath from 'object-path';
356
355
  import MButton from '../mozaic/buttons/MButton.vue';
357
356
  import MContainer from '../mozaic/grid/MContainer.vue';
358
357
  import MDialog from '../mozaic/dialog/MDialog.vue';
@@ -366,6 +365,7 @@ import PbCard from '../cards/PbCard.vue';
366
365
  import PbStickyFooter from '../sticky-footer/PbStickyFooter.vue';
367
366
  import { sortAnswers } from './sortAnswers';
368
367
  import { areConditionsValid } from '../../services/scenarioConditionals';
368
+ import { decorate } from '@/services/decorate';
369
369
  import {
370
370
  ScenarioStepAnswer,
371
371
  ScenarioStepAnswerDialog,
@@ -383,54 +383,6 @@ const BACK_ICON =
383
383
  const INFO_ICON =
384
384
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Notification_Information_24px.svg';
385
385
 
386
- // Function used by the scenario conditions
387
- const getAnswerValue = (answers: Map<string, ScenarioStepAnswer[]>) => {
388
- return (answerCode: string, path: string) => {
389
- let answerValue: ScenarioStepAnswer | undefined = undefined;
390
- if (answers?.get(answerCode)) {
391
- const answerValues = answers.get(answerCode) ?? [];
392
- answerValue = objectPath.get(answerValues[0], path);
393
- }
394
- return answerValue;
395
- };
396
- };
397
-
398
- export const doEval = (
399
- answers: Map<string, ScenarioStepAnswer[]>,
400
- valueToEval: string,
401
- defaultValue: string,
402
- runtimeOptions: any,
403
- ) => {
404
- return new Function('getAnswerValue', 'answers', 'defaultValue', 'runtimeOptions', `return ${valueToEval}`).call(
405
- this,
406
- getAnswerValue(answers),
407
- Object.fromEntries(answers),
408
- defaultValue,
409
- runtimeOptions,
410
- );
411
- };
412
-
413
- //TODO Think to move this kind of function in something like "helpers" or "tools"
414
- // because this is use in multiple components (as PbIncrementalAmountInput.vue),
415
- // and it shouldn't be in a component.
416
- export const decorate = (
417
- answers: Map<string, ScenarioStepAnswer[]>,
418
- runtimeOptions = {},
419
- valueToDecorate: string,
420
- defaultValue = '',
421
- ): string => {
422
- let decoratedValue = valueToDecorate;
423
- if (valueToDecorate) {
424
- const stringToEval = `\`${valueToDecorate}\``;
425
- try {
426
- decoratedValue = doEval(answers, stringToEval, defaultValue, runtimeOptions);
427
- } catch (error) {
428
- decoratedValue = valueToDecorate || defaultValue;
429
- }
430
- }
431
- return decoratedValue;
432
- };
433
-
434
386
  export default defineComponent({
435
387
  name: 'PbQuestion',
436
388
 
@@ -63,7 +63,7 @@ import MTextInput from './../../mozaic/text-input/MTextInput.vue';
63
63
  import DEFAULT_PAYLOAD from './default-payload.json';
64
64
  import { AmountInputPayload } from '@/components/question/amount-input/AmountInput';
65
65
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
66
- import { decorate } from '@/components/question/PbQuestion.vue';
66
+ import { decorate } from '@/services/decorate';
67
67
 
68
68
  const BACK_ICON =
69
69
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -106,14 +106,13 @@ import debounce from 'lodash.debounce';
106
106
  import merge from 'lodash.merge';
107
107
  import initGoogleMapsApi from './google-maps-api';
108
108
  import axios from 'axios';
109
- import objectPath from 'object-path';
110
109
  import MFlex from '../../mozaic/flex/MFlex.vue';
111
110
  import MButton from '../../mozaic/buttons/MButton.vue';
112
111
  import MLink from '../../mozaic/link/MLink.vue';
113
112
  import MTextInput from '../../mozaic/text-input/MTextInput.vue';
114
113
  import DEFAULT_PAYLOAD from './default-payload.json';
115
114
  import { DebouncedFunc } from 'lodash-es';
116
- import { decorate } from '@/components/question/PbQuestion.vue';
115
+ import { decorate } from '@/services/decorate';
117
116
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
118
117
 
119
118
  const BACK_ICON =
@@ -165,7 +165,6 @@ import MLink from '../../../components/mozaic/link/MLink.vue';
165
165
  import MNotification from '../../../components/mozaic/notifications/MNotification.vue';
166
166
  import PbCard from '../../../components/cards/PbCard.vue';
167
167
  import { format } from 'date-fns';
168
- import objectPath from 'object-path';
169
168
  import {
170
169
  ConfigurationsImportMultiSelectAction,
171
170
  ConfigurationsImportPayload,
@@ -174,7 +173,7 @@ import {
174
173
  } from '@/components/question/configurations-import/ConfigurationsImport';
175
174
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
176
175
  import { Configuration } from '@/types/pb/Configuration';
177
- import { decorate } from '@/components/question/PbQuestion.vue';
176
+ import { decorate } from '@/services/decorate';
178
177
 
179
178
  const BACK_ICON =
180
179
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -101,7 +101,7 @@ import MIcon from '../../mozaic/icon/MIcon.vue';
101
101
  import { ref } from 'vue';
102
102
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
103
103
  import { DimensionsInputPayload } from '@/components/question/dimensions-input/DimensionsInput';
104
- import { decorate } from '@/components/question/PbQuestion.vue';
104
+ import { decorate } from '@/services/decorate';
105
105
 
106
106
  const BACK_ICON =
107
107
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -72,7 +72,7 @@ import MLink from '../../mozaic/link/MLink.vue';
72
72
  import MButton from '../../mozaic/buttons/MButton.vue';
73
73
  import { ref } from 'vue';
74
74
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
75
- import { decorate } from '@/components/question/PbQuestion.vue';
75
+ import { decorate } from '@/services/decorate';
76
76
  import MQuantitySelector from '../../mozaic/quantityselector/MQuantitySelector.vue';
77
77
  import { IncrementalAmountPayload } from '@/components/question/incremental-amount-input/IncrementalAmount';
78
78
 
@@ -169,9 +169,6 @@ export default defineComponent({
169
169
  let tempPayload = cloneDeep(DEFAULT_PAYLOAD);
170
170
  tempPayload = merge(tempPayload, props.payload);
171
171
 
172
- // This will doEval and decorate dynamically variables from JSON templates
173
- // For instance:
174
- // "${getAnswerValue('DEVELOPER', 'code') == 'YES' ? 'I'm developer' : 'I'm not developer'}"
175
172
  tempPayload.viewModel.label = decorate(props.answers!, props.runtimeOptions, tempPayload.viewModel.label);
176
173
  tempPayload.viewModel.subtitle = decorate(props.answers!, props.runtimeOptions, tempPayload.viewModel.subtitle);
177
174
 
@@ -75,15 +75,13 @@
75
75
  <script lang="ts">
76
76
  import { defineComponent, PropType } from 'vue';
77
77
  import { ref, computed, onMounted, Ref } from 'vue';
78
- import objectPath from 'object-path';
79
78
  import MFlex from '../../mozaic/flex/MFlex.vue';
80
79
  import MButton from '../../mozaic/buttons/MButton.vue';
81
80
  import MLink from '../../mozaic/link/MLink.vue';
82
81
  import PbItemsList from '../../items/PbItemsList.vue';
83
82
  import { sortAnswers } from '../sortAnswers';
84
83
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
85
- import { Item } from '@/types/pb/Item';
86
- import { decorate } from '@/components/question/PbQuestion.vue';
84
+ import { decorate } from '@/services/decorate';
87
85
 
88
86
  const BACK_ICON =
89
87
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -60,7 +60,7 @@ import MToggle from '../../mozaic/toggle/MToggle.vue';
60
60
  import DEFAULT_PAYLOAD from './default-payload.json';
61
61
  import { NameInputPayload } from '@/components/question/name-input/NameInput';
62
62
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
63
- import { decorate } from '@/components/question/PbQuestion.vue';
63
+ import { decorate } from '@/services/decorate';
64
64
 
65
65
  const BACK_ICON =
66
66
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -146,7 +146,7 @@ import MDialog from '../../mozaic/dialog/MDialog.vue';
146
146
  import DEFAULT_PAYLOAD from './default-payload.json';
147
147
  import { SpaceInputPayload } from '@/components/question/space-input/SpaceInput';
148
148
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
149
- import { decorate } from '@/components/question/PbQuestion.vue';
149
+ import { decorate } from '@/services/decorate';
150
150
 
151
151
  const BACK_ICON =
152
152
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -139,7 +139,6 @@
139
139
 
140
140
  <script lang="ts">
141
141
  import { defineComponent, computed, onMounted, ref, PropType, Ref, ComponentCustomProperties } from 'vue';
142
- import objectPath from 'object-path';
143
142
  import MButton from '../../mozaic/buttons/MButton.vue';
144
143
  import MContainer from '../../mozaic/grid/MContainer.vue';
145
144
  import MFlex from '../../mozaic/flex/MFlex.vue';
@@ -156,7 +155,7 @@ import {
156
155
  UploadDocumentViewModel,
157
156
  } from '@/components/question/upload-document/UploadDocument';
158
157
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
159
- import { decorate, doEval } from '@/components/question/PbQuestion.vue';
158
+ import { decorate, doEval } from '@/services/decorate';
160
159
 
161
160
  const BACK_ICON =
162
161
  'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
@@ -6,7 +6,7 @@ import store from './../../stores/store';
6
6
  import { rest } from 'msw';
7
7
  import { successResolver, internalServerErrorResolver } from '../../services/api/mocks/commonMock';
8
8
  import { getProjectsResolver } from '../../services/api/mocks/projectsMock';
9
- import DEFAULT_PAYLOAD from './restitution-default.json';
9
+ import DEFAULT_PAYLOAD from './restitution-list-default.json';
10
10
  import NO_PRICE_BAR from './restitution-no-pricebar.json';
11
11
  import SAVE_ITEM from './restitution-with-save-item.json';
12
12
  import EXIT_OPTIONS from './restitution-with-exit-options.json';
@@ -131,7 +131,7 @@ export const TemplateSandbox = (args, { argTypes }) => ({
131
131
  <Canvas>
132
132
  <Story
133
133
  name="101 Sandbox"
134
- args={{ payload: DEFAULT_PAYLOAD }}
134
+ args={{ payload: DEFAULT_PAYLOAD, runtimeOptions: { isLoggedIn: true } }}
135
135
  parameters={{
136
136
  msw: [
137
137
  rest.get('/api/inhabitant-projects', getProjectsResolver),
@@ -15,7 +15,7 @@
15
15
  @click.once="$emit('go-back')"
16
16
  />
17
17
  </m-flex>
18
- <div class="pb-restitution-list__title">Votre estimation par travaux</div>
18
+ <div class="pb-restitution-list__title">{{ payload?.viewModel?.title }}</div>
19
19
 
20
20
  <div class="pb-restitution-list__line">
21
21
  <pb-restitution-list-block
@@ -26,9 +26,31 @@
26
26
  ></pb-restitution-list-block>
27
27
  </div>
28
28
 
29
+ <m-flex class="pb-restitution-list__notifications" :direction="'column'" v-if="notifications.length > 0">
30
+ <m-flex
31
+ v-for="(notification, index) in notifications"
32
+ :key="`${index}-${notification.title}`"
33
+ direction="column"
34
+ align-items="center"
35
+ class="pb-restitution-list__notifications__container"
36
+ >
37
+ <m-notification
38
+ :title="notification.title"
39
+ :type="notification.type"
40
+ :text="notification.text"
41
+ :link-label="notification.linkLabel"
42
+ @link-click="handleLinkClicked(notification.linkHref)"
43
+ />
44
+ </m-flex>
45
+ </m-flex>
46
+
29
47
  <m-flex class="pb-restitution-list__footer" direction="row" align-items="center" justify-content="center">
30
- <div class="pb-restitution-list__footer__call">
31
- <m-button label="Être rappelé par un expert"></m-button>
48
+ <div class="pb-restitution-list__footer__call" v-for="button in payload.callToActions" :key="button.label">
49
+ <m-button
50
+ :label="button.label"
51
+ @click="callAction(button.action)"
52
+ :theme="button.bordered ? 'bordered' : 'solid'"
53
+ />
32
54
  </div>
33
55
  <div class="pb-restitution-list__footer__save">
34
56
  <pb-project-item-save
@@ -37,7 +59,6 @@
37
59
  @dialog-close="handleDialogClose"
38
60
  v-if="isLoggedIn"
39
61
  />
40
- <!-- <m-button label="Enregistrer mon estimation" theme="bordered"></m-button> -->
41
62
  </div>
42
63
  </m-flex>
43
64
 
@@ -49,16 +70,16 @@
49
70
  @update:show-dialog="handleShowDialog"
50
71
  >
51
72
  <template #body>
52
- <m-flex class="pb-restitution-list__modal__details" direction="column">
73
+ <div class="pb-restitution-list__modal__details" direction="column">
53
74
  <div class="pb-restitution-list__modal__details__title" v-html="summary.subprojectTemplateLabel"></div>
54
75
 
55
76
  <div class="pb-restitution-list__modal__details__row">
56
- <m-flex class="pb-restitution-list__modal__details__row__title" justify-content="space-between">
77
+ <div class="pb-restitution-list__modal__details__row__title" justify-content="space-between">
57
78
  <div v-html="summary.components[0].title"></div>
58
79
  <div>
59
80
  <strong>{{ summary.components[0].cost.min }}€ - {{ summary.components[0].cost.max }}€</strong>
60
81
  </div>
61
- </m-flex>
82
+ </div>
62
83
 
63
84
  <div v-for="line in summary.components" :key="line.details">
64
85
  <div class="pb-restitution-list__modal__details__row__label" v-if="line.details">
@@ -83,7 +104,7 @@
83
104
  </div>
84
105
 
85
106
  <div v-for="line in summary.components[0].lines" :key="line.text">
86
- <m-flex
107
+ <div
87
108
  v-if="line.cost.reduce"
88
109
  class="pb-restitution-list__modal__details__row__title"
89
110
  :class="{ ' help border-bottom': line.cost.reduce }"
@@ -100,21 +121,20 @@
100
121
  <strong>{{ line.cost.reduce }}€</strong>
101
122
  </div>
102
123
  </div>
103
- </m-flex>
124
+ </div>
104
125
  </div>
105
126
  </div>
106
127
 
107
- <m-flex
108
- class="pb-restitution-list__modal__details__totals"
109
- justify-content="space-between"
110
- align-items="center"
111
- >
128
+ <div class="pb-restitution-list__modal__details__totals" justify-content="space-between" align-items="center">
112
129
  <div><strong>Montant total</strong> (aides déduites)</div>
130
+ <div class="space-mx">entre</div>
113
131
  <div>
114
- entre <strong>{{ summary.cost.min }}€</strong> et <strong>{{ summary.cost.max }}€</strong>
132
+ <strong>{{ summary.cost.min }}€</strong>
133
+ <div class="space-mx">et</div>
134
+ <strong>{{ summary.cost.max }}€</strong>
115
135
  </div>
116
- </m-flex>
117
- </m-flex>
136
+ </div>
137
+ </div>
118
138
  </template>
119
139
  <template #footer>
120
140
  <m-flex class="pb-restitution-list__modal__footer" align-items="center" justify-content="center">
@@ -133,11 +153,11 @@ import MDialog from '../mozaic/dialog/MDialog.vue';
133
153
  import MFlex from '../mozaic/flex/MFlex.vue';
134
154
  import MLink from '../mozaic/link/MLink.vue';
135
155
  import PbProjectItemSave from '../projects/project-item-save/PbProjectItemSave.vue';
136
-
137
- import { RestitutionPayload } from '@/types/pb/Restitution';
156
+ import MNotification from '../mozaic/notifications/MNotification.vue';
157
+ import { NotificationOptions } from '@/types/pb/Notification';
158
+ import { RestitutionPayload, RestitutionPayloadAction } from '@/types/pb/Restitution';
138
159
  import { ScenarioStepAnswer } from '@/types/pb/Scenario';
139
160
  import PbRestitutionListBlock from './PbRestitutionListBlock.vue';
140
- import { NotificationOptions } from '@/types/pb/Notification';
141
161
  import { Project } from '../../types/pb/Project';
142
162
 
143
163
  export default defineComponent({
@@ -148,6 +168,7 @@ export default defineComponent({
148
168
  MLink,
149
169
  PbRestitutionListBlock,
150
170
  PbProjectItemSave,
171
+ MNotification,
151
172
  },
152
173
  data() {
153
174
  return {
@@ -201,15 +222,14 @@ export default defineComponent({
201
222
  computed: {
202
223
  ...mapGetters('estimates', { summary: 'getSummary' }),
203
224
  isLoggedIn() {
204
- return true;
225
+ return this.runtimeOptions.isLoggedIn === true;
205
226
  },
206
227
  },
207
228
  created() {
208
229
  if (this.payload?.callToActions) {
209
- const saveAction = Object.values(this.payload.callToActions).find((cta) => {
210
- cta.action?.component === 'PbProjectItemSave';
211
- });
212
- this.showSaveProjectItem = this.showSaveEstimate;
230
+ if (this.payload?.callToActions) {
231
+ this.showSaveProjectItem = this.showSaveEstimate;
232
+ }
213
233
  }
214
234
  },
215
235
  methods: {
@@ -238,20 +258,41 @@ export default defineComponent({
238
258
  linkLabel: 'Voir dans le projet',
239
259
  });
240
260
  },
261
+ handleDialogClose() {
262
+ this.showSaveProjectItem = false;
263
+ },
264
+ callAction(action: RestitutionPayloadAction) {
265
+ if (action.type === 'MODAL') {
266
+ if (action.component === 'PbProjectItemSave') {
267
+ if (this.runtimeOptions.isLoggedIn === true) {
268
+ this.showSaveProjectItem = true;
269
+ }
270
+
271
+ this.$emit('click-save-item', {
272
+ answers: [],
273
+ action: action,
274
+ });
275
+ }
276
+ } else {
277
+ this.$emit('step-completed', {
278
+ answers: [],
279
+ nextStep: action,
280
+ });
281
+ }
282
+ },
241
283
  },
242
284
  });
243
285
  </script>
244
286
 
245
287
  <style lang="scss" scoped>
246
288
  @import 'pb-variables';
247
-
289
+ $responsive-breakpoint: 'm';
248
290
  .hidden {
249
291
  display: none;
250
292
  }
251
293
 
252
294
  .pb-restitution-list {
253
295
  @include set-font-face('regular');
254
-
255
296
  align-items: center;
256
297
  display: flex;
257
298
  flex-direction: column;
@@ -259,12 +300,17 @@ export default defineComponent({
259
300
  margin: 0 auto;
260
301
  padding-bottom: $mu250;
261
302
  position: relative;
262
- width: calc(100% - #{$mu050} - #{$mu050});
303
+ width: 100%;
304
+
305
+ @include set-from-screen($responsive-breakpoint) {
306
+ justify-content: space-between;
307
+ width: calc(100% - #{$mu050} - #{$mu050});
308
+ }
263
309
 
264
310
  &__back-button {
265
311
  align-self: flex-start;
266
312
  opacity: 1;
267
- padding: $mu100 0 $mu100 $mu025;
313
+ padding: $mu100 0 $mu100 $mu100;
268
314
  transition: opacity 0.15s 0.5s;
269
315
  z-index: 2;
270
316
 
@@ -282,7 +328,11 @@ export default defineComponent({
282
328
  position: absolute;
283
329
  right: 0;
284
330
  text-align: center;
285
- top: 0;
331
+ top: $mu250;
332
+
333
+ @include set-from-screen($responsive-breakpoint) {
334
+ top: 0;
335
+ }
286
336
  }
287
337
 
288
338
  &__footer {
@@ -324,6 +374,14 @@ export default defineComponent({
324
374
  width: 100%;
325
375
 
326
376
  &__title {
377
+ @include set-from-screen($responsive-breakpoint) {
378
+ display: flex;
379
+ justify-content: space-between;
380
+ }
381
+
382
+ & > div {
383
+ margin-bottom: $mu050;
384
+ }
327
385
  &.help {
328
386
  margin-bottom: $mu150;
329
387
  }
@@ -356,30 +414,55 @@ export default defineComponent({
356
414
  list-style: disc;
357
415
 
358
416
  ul {
359
- padding: 0 0 0 $mu100;
417
+ padding: 0;
418
+
419
+ @include set-from-screen($responsive-breakpoint) {
420
+ padding: 0 0 0 $mu100;
421
+ }
360
422
  }
361
423
 
362
424
  li {
363
425
  display: flex;
364
- justify-content: space-between;
426
+ flex-direction: column;
365
427
  margin-bottom: $mu075;
366
428
 
429
+ @include set-from-screen($responsive-breakpoint) {
430
+ flex-direction: row;
431
+ justify-content: space-between;
432
+ }
433
+
367
434
  &::before {
368
435
  content: '•';
436
+ display: none;
369
437
  margin-right: 5px;
438
+
439
+ @include set-from-screen($responsive-breakpoint) {
440
+ display: inherit;
441
+ justify-content: space-between;
442
+ }
370
443
  }
371
444
 
372
445
  div {
373
446
  &:first-child {
374
447
  @include set-font-face('regular');
375
448
  @include set-font-scale('05', 's');
376
- width: 60%;
449
+ margin-bottom: $mu050;
450
+ width: 100%;
451
+
452
+ @include set-from-screen($responsive-breakpoint) {
453
+ margin-bottom: 0;
454
+ width: 60%;
455
+ }
377
456
  }
378
457
 
379
458
  &:last-child {
380
459
  @include set-font-face('semi-bold');
381
- text-align: right;
382
- width: 40%;
460
+ width: 100%;
461
+
462
+ @include set-from-screen($responsive-breakpoint) {
463
+ text-align: right;
464
+ width: 40%;
465
+ }
383
466
  }
384
467
  }
385
468
  }
@@ -389,6 +472,24 @@ export default defineComponent({
389
472
  &__totals {
390
473
  width: 100%;
391
474
 
475
+ .space-mx {
476
+ margin-right: 5px;
477
+
478
+ @include set-from-screen($responsive-breakpoint) {
479
+ margin: 0 5px;
480
+ }
481
+ }
482
+
483
+ & > div {
484
+ align-items: center;
485
+ display: flex;
486
+ }
487
+
488
+ @include set-from-screen($responsive-breakpoint) {
489
+ align-items: center;
490
+ display: flex;
491
+ }
492
+
392
493
  div {
393
494
  &:last-child {
394
495
  strong {
@@ -403,5 +504,27 @@ export default defineComponent({
403
504
  padding: $mu150 0;
404
505
  }
405
506
  }
507
+
508
+ &__notifications {
509
+ width: 100%;
510
+
511
+ @include set-from-screen($responsive-breakpoint) {
512
+ width: 710px;
513
+ }
514
+
515
+ &__container {
516
+ margin: auto;
517
+ min-width: 90%;
518
+
519
+ @include set-from-screen($responsive-breakpoint) {
520
+ min-width: 100%;
521
+ }
522
+ }
523
+ }
524
+ }
525
+
526
+ .m-notification {
527
+ margin-top: $mu075;
528
+ width: 100%;
406
529
  }
407
530
  </style>
@@ -4,13 +4,13 @@
4
4
  <div class="pb-restitution-list-block__title" v-html="summary.subprojectTemplateLabel"></div>
5
5
 
6
6
  <div class="pb-restitution-list-block__body" v-if="payload">
7
- <m-flex class="pb-restitution-list-line" justify-content="space-between">
7
+ <div class="pb-restitution-list-line">
8
8
  <div class="pb-restitution-list-line__title" v-html="summary.components[0].title"></div>
9
- <m-flex class="pb-restitution-list-line__price" justify-content="flex-end" wrap>
9
+ <div class="pb-restitution-list-line__price">
10
10
  <div class="pb-restitution-list-line__price__min">{{ summary.components[0].cost.min }}€&nbsp;-&nbsp;</div>
11
11
  <div class="pb-restitution-list-line__price__max">{{ summary.components[0].cost.max }}€</div>
12
- </m-flex>
13
- </m-flex>
12
+ </div>
13
+ </div>
14
14
 
15
15
  <pb-restitution-list-line
16
16
  v-for="line in payload.lines"
@@ -19,18 +19,15 @@
19
19
  :line="line"
20
20
  ></pb-restitution-list-line>
21
21
 
22
- <m-flex class="pb-restitution-list-block__body__row unbordered">
23
- <m-flex
24
- class="pb-restitution-list-block__body__row__title full"
25
- align-items="center"
26
- justify-content="space-between"
27
- >
22
+ <div class="pb-restitution-list-block__body__row unbordered">
23
+ <div class="pb-restitution-list-block__body__row__title full">
28
24
  <div><strong>Montant total</strong> (aides déduites)</div>
29
25
  <div class="pb-restitution-list-block__body__row__title__amount">
30
- entre <strong>{{ summary.cost.min }}€</strong> et <strong>{{ summary.cost.max }}€</strong>
26
+ <div>entre</div>
27
+ <strong>{{ summary.cost.min }}€</strong> et <strong>{{ summary.cost.max }}€</strong>
31
28
  </div>
32
- </m-flex>
33
- </m-flex>
29
+ </div>
30
+ </div>
34
31
 
35
32
  <m-button
36
33
  theme="bordered-neutral"
@@ -78,12 +75,18 @@ export default defineComponent({
78
75
 
79
76
  <style lang="scss" scoped>
80
77
  @import 'pb-variables';
78
+ $responsive-breakpoint: 'm';
81
79
 
82
80
  .pb-restitution-list-block {
83
81
  @include set-box-shadow('l');
84
82
  margin-bottom: $mu250;
85
- padding: $mu250;
86
- width: 630px;
83
+ padding: $mu150;
84
+ width: 80%;
85
+
86
+ @include set-from-screen($responsive-breakpoint) {
87
+ padding: $mu250;
88
+ width: 630px;
89
+ }
87
90
 
88
91
  &__title {
89
92
  @include set-font-face('semi-bold');
@@ -110,18 +113,36 @@ export default defineComponent({
110
113
 
111
114
  &__title {
112
115
  @include set-font-face('regular');
113
- width: 70%;
116
+ width: 100%;
117
+
118
+ @include set-from-screen($responsive-breakpoint) {
119
+ width: 70%;
120
+ }
114
121
 
115
122
  &.full {
116
- display: grid;
123
+ display: flex;
124
+ flex-direction: column;
117
125
  width: 100%;
118
126
  @include set-font-scale('06', 's');
127
+
128
+ @include set-from-screen($responsive-breakpoint) {
129
+ align-items: center;
130
+ flex-direction: row;
131
+ }
119
132
  }
120
133
 
121
134
  &__amount {
122
135
  @include set-font-scale('06', 's');
123
- justify-self: flex-end;
124
- margin-left: $mu100;
136
+
137
+ @include set-from-screen($responsive-breakpoint) {
138
+ align-items: center;
139
+ display: flex;
140
+ margin-left: $mu050;
141
+
142
+ strong {
143
+ margin: 0 5px;
144
+ }
145
+ }
125
146
 
126
147
  strong {
127
148
  @include set-font-scale('07', 's');
@@ -132,8 +153,14 @@ export default defineComponent({
132
153
  &__price {
133
154
  @include set-font-face('semi-bold');
134
155
  @include set-font-scale('05', 's');
135
- text-align: right;
136
- width: 30%;
156
+ display: flex;
157
+ width: 100%;
158
+
159
+ @include set-from-screen($responsive-breakpoint) {
160
+ flex-direction: row;
161
+ text-align: right;
162
+ width: 30%;
163
+ }
137
164
  }
138
165
  }
139
166
  }
@@ -141,19 +168,35 @@ export default defineComponent({
141
168
 
142
169
  .pb-restitution-list-line {
143
170
  border-bottom: 1px solid $color-grey-500;
171
+ display: flex;
172
+ flex-direction: column;
144
173
  padding: $mu150 0 $mu075 0;
145
174
 
175
+ @include set-from-screen($responsive-breakpoint) {
176
+ flex-direction: row;
177
+ }
178
+
146
179
  &__title {
147
180
  @include set-font-face('regular');
148
181
  @include set-font-scale('06', 's');
149
- width: 70%;
182
+ margin-bottom: $mu050;
183
+ width: 100%;
184
+
185
+ @include set-from-screen($responsive-breakpoint) {
186
+ margin-bottom: 0;
187
+ }
150
188
  }
151
189
 
152
190
  &__price {
153
191
  @include set-font-face('semi-bold');
154
192
  @include set-font-scale('06', 's');
155
- text-align: right;
156
- width: 30%;
193
+ display: flex;
194
+ width: 100%;
195
+
196
+ @include set-from-screen($responsive-breakpoint) {
197
+ text-align: right;
198
+ width: 30%;
199
+ }
157
200
 
158
201
  strong {
159
202
  @include set-font-scale('07', 's');
@@ -1,21 +1,14 @@
1
1
  <template>
2
- <m-flex
3
- class="pb-restitution-list-line"
4
- :class="{ green: line.type === 'REDUCTION' }"
5
- justify-content="space-between"
6
- v-if="line.type"
7
- >
2
+ <div class="pb-restitution-list-line" :class="{ green: line.type === 'REDUCTION' }" v-if="line.type">
8
3
  <div class="pb-restitution-list-line__title" v-html="line.text"></div>
9
- <m-flex class="pb-restitution-list-line__price" justify-content="flex-end" wrap v-if="line.type != 'REDUCTION'">
4
+ <div class="pb-restitution-list-line__price" v-if="line.type != 'REDUCTION'">
10
5
  <div class="pb-restitution-list-line__price__min">
11
6
  {{ formatPriceRange(line.cost.min, line.cost.max).min }}€&nbsp;-&nbsp;
12
7
  </div>
13
8
  <div class="pb-restitution-list-line__price__max">{{ formatPriceRange(line.cost.min, line.cost.max).max }}€</div>
14
- </m-flex>
15
- <m-flex class="pb-restitution-list-line__price" justify-content="flex-end" wrap v-else>
16
- {{ line.cost.reduce }}€
17
- </m-flex>
18
- </m-flex>
9
+ </div>
10
+ <div class="pb-restitution-list-line__price">{{ line.cost.reduce }}€</div>
11
+ </div>
19
12
  </template>
20
13
 
21
14
  <script lang="ts">
@@ -47,22 +40,43 @@ export default defineComponent({
47
40
 
48
41
  <style lang="scss" scoped>
49
42
  @import 'pb-variables';
43
+ $responsive-breakpoint: 'm';
50
44
 
51
45
  .pb-restitution-list-line {
52
46
  border-bottom: 1px solid $color-grey-500;
47
+ display: flex;
48
+ flex-direction: column;
53
49
  padding: $mu150 0 $mu075 0;
54
50
 
51
+ @include set-from-screen($responsive-breakpoint) {
52
+ flex-direction: row;
53
+ justify-content: space-between;
54
+ }
55
+
55
56
  &__title {
56
57
  @include set-font-face('regular');
57
58
  @include set-font-scale('06', 's');
58
- width: 70%;
59
+ margin-bottom: $mu050;
60
+ width: 100%;
61
+
62
+ @include set-from-screen($responsive-breakpoint) {
63
+ margin-bottom: 0;
64
+ text-align: left;
65
+ width: 70%;
66
+ }
59
67
  }
60
68
 
61
69
  &__price {
62
70
  @include set-font-face('semi-bold');
63
71
  @include set-font-scale('06', 's');
64
- text-align: right;
65
- width: 30%;
72
+ display: flex;
73
+ width: 100%;
74
+
75
+ @include set-from-screen($responsive-breakpoint) {
76
+ justify-content: flex-end;
77
+ text-align: right;
78
+ width: 30%;
79
+ }
66
80
 
67
81
  strong {
68
82
  @include set-font-scale('07', 's');
@@ -0,0 +1,24 @@
1
+ {
2
+ "viewModel": {
3
+ "title": "Votre estimation par travaux"
4
+ },
5
+ "callToActions": [
6
+ {
7
+ "type": "button",
8
+ "label": "Être rappelé par un expert",
9
+ "href": "",
10
+ "bordered": false
11
+ },
12
+ {
13
+ "conditions": ["!runtimeOptions.projectMode && !runtimeOptions.estimateCreated"],
14
+ "type": "button",
15
+ "label": "J'enregistre mon estimation",
16
+ "action": {
17
+ "type": "MODAL",
18
+ "code": "SUMMARY",
19
+ "component": "PbProjectItemSave"
20
+ },
21
+ "bordered": true
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,65 @@
1
+ import objectPath from 'object-path';
2
+
3
+ import { ScenarioStepAnswer } from '@/types/pb/Scenario';
4
+
5
+ /**
6
+ * This function will doEval and decorate dynamically variables from JSON templates.
7
+ *
8
+ * For instance:
9
+ * "${getAnswerValue('DEVELOPER', 'code') == 'YES' ? 'I'm developer' : 'I'm not developer'}".
10
+ *
11
+ * @param answers A map of string and an array of ScenarioStepAnswer.
12
+ * @param valueToDecorate Targeted value to decorate.
13
+ * @param defaultValue Empty string by default.
14
+ * @returns Decorated value.
15
+ */
16
+ export function decorate(
17
+ answers: Map<string, ScenarioStepAnswer[]>,
18
+ runtimeOptions = {},
19
+ valueToDecorate: string,
20
+ defaultValue = '',
21
+ ): string {
22
+ let decoratedValue = valueToDecorate;
23
+ if (valueToDecorate) {
24
+ const stringToEval = `\`${valueToDecorate}\``;
25
+ try {
26
+ decoratedValue = doEval(answers, stringToEval, defaultValue, runtimeOptions);
27
+ } catch (error) {
28
+ decoratedValue = valueToDecorate || defaultValue;
29
+ }
30
+ }
31
+ return decoratedValue;
32
+ }
33
+
34
+ export function doEval(
35
+ this: any,
36
+ answers: Map<string, ScenarioStepAnswer[]>,
37
+ valueToEval: string,
38
+ defaultValue: string,
39
+ runtimeOptions: unknown,
40
+ ): any {
41
+ return new Function('getAnswerValue', 'answers', 'defaultValue', 'runtimeOptions', `return ${valueToEval}`).call(
42
+ this,
43
+ getAnswerValue(answers),
44
+ Object.fromEntries(answers),
45
+ defaultValue,
46
+ runtimeOptions,
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Function used by the scenario conditions.
52
+ *
53
+ * @param answers A map of string and an array of ScenarioStepAnswer.
54
+ * @returns Answers values.
55
+ */
56
+ function getAnswerValue(answers: Map<string, ScenarioStepAnswer[]>) {
57
+ return (answerCode: string, path: string) => {
58
+ let answerValue: ScenarioStepAnswer | undefined = undefined;
59
+ if (answers?.get(answerCode)) {
60
+ const answerValues = answers.get(answerCode) ?? [];
61
+ answerValue = objectPath.get(answerValues[0], path);
62
+ }
63
+ return answerValue;
64
+ };
65
+ }