project-booster-vue 10.26.0 → 10.27.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.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/src/components/date-picker/MPbDatePicker.vue +1 -1
  3. package/src/components/projects/project-attributes/PbProjectAttributes.vue +26 -26
  4. package/src/components/projects/project-item-save/PbProjectItemSave.vue +1 -0
  5. package/src/components/question/amount-input/AmountInput.ts +22 -1
  6. package/src/components/question/space-input/SpaceInput.ts +2 -1
  7. package/src/components/rework/media/upload/MPbMediaUpload.vue +3 -3
  8. package/src/components/rework/navigation/MPbNavigation.vue +1 -1
  9. package/src/components/rework/question/amount-input/AmountInput.ts +67 -0
  10. package/src/components/rework/question/amount-input/MPbAmountInput.stories.mdx +321 -0
  11. package/src/components/rework/question/amount-input/MPbAmountInput.vue +499 -0
  12. package/src/components/rework/question/amount-input/default-payload.json +16 -0
  13. package/src/components/rework/question/name-input/MPbNameInput.vue +2 -1
  14. package/src/components/rework/question/space-input/MPbSpaceInput.vue +16 -5
  15. package/src/components/rework/question/space-input/SpaceInput.ts +13 -0
  16. package/src/components/rework/question/upload-document/MPbUploadDocument.vue +3 -3
  17. package/src/components/rework/services/event.ts +1 -1
  18. package/src/components/rework/styles/global.scss +2 -1
  19. package/src/components/rework/ui/accordion/MPbAccordion.stories.mdx +37 -0
  20. package/src/components/rework/ui/accordion/MPbAccordion.vue +88 -0
  21. package/src/components/rework/ui/accordion/default-payload.json +9 -0
  22. package/src/components/scenario/PbScenario.vue +2 -0
  23. package/src/components/scenario/scenarii/estimator-solar-panel.json +177 -2123
  24. package/src/services/api/trezorApi.ts +1 -1
  25. package/src/types/pb/Scenario.ts +13 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-booster-vue",
3
- "version": "10.26.0",
3
+ "version": "10.27.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -35,7 +35,7 @@ function skipQuestion() {
35
35
  <span class="title">{{ props.payload.viewModel.title }}</span>
36
36
  <span class="subtile">{{ props.payload.viewModel.subtitle }}</span>
37
37
  <input type="date" v-model="moveDate" class="mc-text-input mc-field__input input-text-date" />
38
- <MButton
38
+ <m-button
39
39
  class="button"
40
40
  :label="props.payload.viewModel.actionLabel"
41
41
  @click="handleSubmit"
@@ -315,6 +315,16 @@ export default defineComponent({
315
315
 
316
316
  components: { MFlexy, PbProjectAttribute },
317
317
 
318
+ props: {
319
+ /**
320
+ * The project attributes
321
+ */
322
+ projectAttributes: {
323
+ type: Object,
324
+ default: () => ({}),
325
+ },
326
+ },
327
+
318
328
  data() {
319
329
  return {
320
330
  /**
@@ -515,13 +525,22 @@ export default defineComponent({
515
525
  };
516
526
  },
517
527
 
518
- props: {
519
- /**
520
- * The project attributes
521
- */
522
- projectAttributes: {
523
- type: Object,
524
- default: () => ({}),
528
+ computed: {
529
+ displayedProjectAttributes() {
530
+ return Object.entries(this.projectAttributes);
531
+ },
532
+
533
+ getCommentAttribute(): string | null {
534
+ const commentAttribute = Object.entries(this.projectAttributes).find(
535
+ ([attributeId]) => attributeId === 'comment',
536
+ );
537
+ return commentAttribute ? commentAttribute[1].value : null;
538
+ },
539
+
540
+ isRigidSpaWanted(): boolean {
541
+ return Object.entries(this.projectAttributes).some(
542
+ ([attributeId, attribute]) => attributeId === 'works' && attribute.value.includes('RIGID_SPA'),
543
+ );
525
544
  },
526
545
  },
527
546
 
@@ -547,25 +566,6 @@ export default defineComponent({
547
566
  }
548
567
  },
549
568
  },
550
-
551
- computed: {
552
- displayedProjectAttributes() {
553
- return Object.entries(this.projectAttributes);
554
- },
555
-
556
- getCommentAttribute(): string | null {
557
- const commentAttribute = Object.entries(this.projectAttributes).find(
558
- ([attributeId]) => attributeId === 'comment',
559
- );
560
- return commentAttribute ? commentAttribute[1].value : null;
561
- },
562
-
563
- isRigidSpaWanted(): boolean {
564
- return Object.entries(this.projectAttributes).some(
565
- ([attributeId, attribute]) => attributeId === 'works' && attribute.value.includes('RIGID_SPA'),
566
- );
567
- },
568
- },
569
569
  });
570
570
  </script>
571
571
 
@@ -105,6 +105,7 @@ export const answerGettersMappers: Map<string, (param: any) => void> = new Map<s
105
105
  MPbQuestion: ({ code }: { code: string }): string => code,
106
106
  MPbRestitutionList: ({ code }: { code: string }): string => code,
107
107
  MPbSpaceInput: ({ space }: { space: string }): number => parseFloat(space),
108
+ MPbAmountInput: ({ amount }: { amount: string }): number => parseFloat(amount),
108
109
  MPbNameInput: ({ projectName }: { projectName: string }) => projectName,
109
110
  MPbCitySearch: ({ city }: { city: any }) => city.postalCode,
110
111
  MPbIncrementalAmountInput: ({ value }: { value: string }) => parseInt(value),
@@ -1,21 +1,42 @@
1
+ import { HelpArea, Pedagogy } from '@/components/rework/question/amount-input/AmountInput';
2
+ import { PayloadAction } from '@/components/rework/types/genericPayload';
3
+ import { ScenarioStepSkippableOptions } from '@/types/pb/Scenario';
4
+
1
5
  export interface AmountInputValidation {
2
- notValidNumberMessage: string;
3
6
  minValue: number;
4
7
  minErrorMessage: string;
5
8
  maxValue: number;
6
9
  maxErrorMessage: string;
7
10
  requiredErrorMessage: string;
11
+ notValidTypeMessage: string;
12
+ notValidNumberMessage: string;
8
13
  }
14
+
9
15
  export interface AmountInputViewModel {
10
16
  backLabel: string;
17
+ hideBackButton?: boolean;
11
18
  label: string;
19
+ subtitle: string;
12
20
  placeholder: string;
13
21
  actionLabel: string;
14
22
  validation: AmountInputValidation;
23
+ forceBackButton?: boolean;
24
+ callToActions: PayloadAction[];
25
+ analytics: {
26
+ funnel_name: string;
27
+ };
28
+ footer?: {
29
+ label: string;
30
+ conditions: string[];
31
+ };
32
+ nextStep?: any;
15
33
  }
16
34
 
17
35
  export interface AmountInputPayload {
18
36
  viewModel: AmountInputViewModel;
19
37
  value: { amount: string };
20
38
  defaultDecoratorValue: string;
39
+ skippable?: ScenarioStepSkippableOptions;
40
+ helpArea?: HelpArea[];
41
+ pedagogy?: Pedagogy;
21
42
  }
@@ -1,4 +1,4 @@
1
- import { HelpArea } from '@/components/rework/question/space-input/SpaceInput';
1
+ import { HelpArea, Pedagogy } from '@/components/rework/question/space-input/SpaceInput';
2
2
  import { PayloadAction } from '@/components/rework/types/genericPayload';
3
3
  import { ScenarioStepSkippableOptions } from '@/types/pb/Scenario';
4
4
 
@@ -37,4 +37,5 @@ export interface SpaceInputPayload {
37
37
  defaultDecoratorValue: string;
38
38
  skippable?: ScenarioStepSkippableOptions;
39
39
  helpArea?: HelpArea[];
40
+ pedagogy?: Pedagogy;
40
41
  }
@@ -30,9 +30,9 @@
30
30
  />
31
31
  </div>
32
32
  <div class="pb-media-upload__link" v-if="payload.viewModel.notification">
33
- <m-link :theme="payload.viewModel.notification.theme" @click.prevent="displayNotificationDialog = true">{{
34
- payload.viewModel.notification.title
35
- }}</m-link>
33
+ <m-link :theme="payload.viewModel.notification.theme" @click.prevent="displayNotificationDialog = true">
34
+ {{ payload.viewModel.notification.title }}
35
+ </m-link>
36
36
  </div>
37
37
  <div class="pb-media-upload__buttons" v-if="payload.callToActions">
38
38
  <div class="pb-media-upload__buttons__button" v-for="button in payload.callToActions">
@@ -154,7 +154,7 @@ const emitClickEvent = (nextStep: any) => {
154
154
  }
155
155
  };
156
156
 
157
- const cdlClickDate = computed((): String => {
157
+ const cdlClickDate = computed((): string => {
158
158
  let events = [
159
159
  {
160
160
  attributes: {
@@ -0,0 +1,67 @@
1
+ import { ScenarioStepSkippableOptions } from '@/types/pb/Scenario';
2
+
3
+ export interface AmountInputValidation {
4
+ minValue: number;
5
+ minErrorMessage: string;
6
+ maxValue: number;
7
+ maxErrorMessage: string;
8
+ requiredErrorMessage: string;
9
+ notValidTypeMessage: string;
10
+ }
11
+
12
+ export interface AmountInputViewModel {
13
+ backLabel: string;
14
+ label: string;
15
+ subtitle?: string;
16
+ placeholder: string;
17
+ actionLabel: string;
18
+ validation: AmountInputValidation;
19
+ unitLabel: string;
20
+ hideBackButton?: boolean;
21
+ progressBar?: boolean;
22
+ widthXlSize?: boolean;
23
+ analytics: {
24
+ funnel_name: string;
25
+ };
26
+ footer?: {
27
+ label: string;
28
+ conditions: string[];
29
+ };
30
+ }
31
+
32
+ export interface AmountInputPayload {
33
+ viewModel: AmountInputViewModel;
34
+ value: { space: string };
35
+ defaultDecoratorValue: string;
36
+ skippable?: ScenarioStepSkippableOptions;
37
+ helpArea?: HelpArea[];
38
+ pedagogy?: Pedagogy;
39
+ }
40
+
41
+ export interface HelpArea {
42
+ type: string;
43
+ label: string;
44
+ action: Action;
45
+ }
46
+
47
+ export interface Pedagogy {
48
+ component: string;
49
+ label?: string;
50
+ content?: PedagogyContent[];
51
+ }
52
+
53
+ export interface PedagogyContent {
54
+ htmlContent: string;
55
+ imgUrl: string;
56
+ imgAlignment: string;
57
+ }
58
+
59
+ export interface Action {
60
+ dialogViewModel: DialogViewModel;
61
+ }
62
+
63
+ export interface DialogViewModel {
64
+ watermark: string;
65
+ headerTitle: string;
66
+ htmlContent: string;
67
+ }
@@ -0,0 +1,321 @@
1
+ import { nestedAppDecorator } from '../../../../../.storybook/nested-app-decorator';
2
+ import { Story, Meta, ArgsTable, Source, Canvas } from '@storybook/addon-docs';
3
+ import MPbAmountInput from './MPbAmountInput.vue';
4
+ import DEFAULT_PAYLOAD from './default-payload.json';
5
+
6
+ <Meta
7
+ title="Project Booster/Rework/Questions/MPbAmountInput 🦠"
8
+ component={MPbAmountInput}
9
+ argTypes={{
10
+ 'payload': {
11
+ table: {
12
+ defaultValue: {
13
+ summary: 'object',
14
+ detail: JSON.stringify(DEFAULT_PAYLOAD, null, ' '),
15
+ },
16
+ },
17
+ control: {
18
+ type: 'object',
19
+ },
20
+ },
21
+ 'dynamic event name according to completedEventName prop': {
22
+ table: {
23
+ defaultValue: {
24
+ summary: 'Event payload',
25
+ detail: JSON.stringify({ answers: [{ amount: '80' }] }, null, ' '),
26
+ },
27
+ },
28
+ },
29
+ 'onStepCompleted': {
30
+ action: 'Step completed',
31
+ table: { disable: true },
32
+ },
33
+ }}
34
+ decorators={[nestedAppDecorator({}, [])]}
35
+ parameters={{ layout: 'fullscreen' }}
36
+ />
37
+
38
+ # 🦠 `MPbAmountInput` - Components
39
+
40
+ [![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)
41
+
42
+ ---
43
+
44
+ The `MPbAmountInput` Vue component allows to ask for an amount. It
45
+ is customizable through the payload property. It has the same
46
+ behaviour as the `PbQuestion` component and is designed to work in scenarii.
47
+
48
+ It has a default provided payload, used in the Sandbox below.
49
+
50
+ The input field is automatically focused.
51
+
52
+ The back button will make `vue-router` trigger the history back feature from the browser. It can be hidden with the
53
+ `showBackButton` flag.
54
+
55
+ # `MPbAmountInput` - Component props
56
+
57
+ export const TemplateSandbox = (args, { argTypes }) => ({
58
+ props: Object.keys(argTypes),
59
+ components: { MPbAmountInput },
60
+ setup() {
61
+ return { args };
62
+ },
63
+ template: `<m-pb-amount-input
64
+ style="max-width:720px; margin: auto"
65
+ :payload="args.payload"
66
+ :show-back-button="args.showBackButton"
67
+ :answers="args.answers"
68
+ :runtime-options="args.runtimeOptions"
69
+ :completed-event-name="args.completedEventName"
70
+ @step-completed="args.onStepCompleted"
71
+ />`,
72
+ });
73
+
74
+ <Canvas>
75
+ <Story name="101 Sandbox" args={{ payload: DEFAULT_PAYLOAD }}>
76
+ {TemplateSandbox.bind({})}
77
+ </Story>
78
+ </Canvas>
79
+
80
+ <ArgsTable story="101 Sandbox" />
81
+
82
+ # `MPbAmountInput` - Features
83
+
84
+ ## Customization
85
+
86
+ It is possible to customize the component default behaviour by **overriding** a property in the provided `payload` property.
87
+
88
+ Here is the default payload:
89
+
90
+ <Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD, null, ' ')} />
91
+
92
+ Here is the payload with overriden values:
93
+
94
+ export const overridenLabelsPayload = {
95
+ viewModel: {
96
+ backLabel: 'Retour',
97
+ label: "Quel est le coût moyen mensuel de votre facture d'électricité ?",
98
+ placeholder: 'Coût moyen',
99
+ actionLabel: 'Valider',
100
+ },
101
+ };
102
+
103
+ <Source language="json" code={JSON.stringify(overridenLabelsPayload, null, ' ')} />
104
+
105
+ <Canvas>
106
+ <Story
107
+ name="Showcase - Feature customize"
108
+ parameters={{ controls: { disable: true } }}
109
+ args={{ payload: overridenLabelsPayload }}
110
+ >
111
+ {TemplateSandbox.bind({})}
112
+ </Story>
113
+ </Canvas>
114
+
115
+ ## Custom validation
116
+
117
+ It is possible to customize the input field validation. To do so, provide a `validation` property to the `viewModel`.
118
+
119
+ The default `validation` object is:
120
+
121
+ <Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD.viewModel.validation, null, ' ')} />
122
+
123
+ It is possible to customize those values:
124
+
125
+ export const customValidation = {
126
+ viewModel: {
127
+ validation: {
128
+ maxValue: 100,
129
+ maxErrorMessage: 'Doit être plus petit que 100',
130
+ minValue: 50,
131
+ minErrorMessage: 'Doit être plus grand que 50',
132
+ notValidNumberMessage: 'Veuillez saisir une surface en m²',
133
+ requiredErrorMessage: 'Champ obligatoire',
134
+ },
135
+ },
136
+ };
137
+
138
+ <Source language="json" code={JSON.stringify(customValidation, null, ' ')} />
139
+
140
+ <Canvas>
141
+ <Story
142
+ name="Showcase - Feature custom validation"
143
+ parameters={{ controls: { disable: true }, storyshots: { disable: true } }}
144
+ args={{ payload: customValidation }}
145
+ >
146
+ {TemplateSandbox.bind({})}
147
+ </Story>
148
+ </Canvas>
149
+
150
+ ## Footer
151
+
152
+ It is possible to customize the question wit a footer. To do so, provide a `footer` property to the `viewModel`.
153
+
154
+ <Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD.viewModel.validation, null, ' ')} />
155
+
156
+ It is possible to customize those values:
157
+
158
+ export const customFooter = {
159
+ viewModel: {
160
+ footer: {
161
+ label:
162
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
163
+ },
164
+ },
165
+ };
166
+
167
+ <Source language="json" code={JSON.stringify(customFooter, null, ' ')} />
168
+
169
+ <Canvas>
170
+ <Story
171
+ name="Showcase - Feature custom footer"
172
+ parameters={{ controls: { disable: true } }}
173
+ args={{ payload: customFooter }}
174
+ >
175
+ {TemplateSandbox.bind({})}
176
+ </Story>
177
+ </Canvas>
178
+
179
+ ## With modal support
180
+
181
+ export const withModal = {
182
+ viewModel: {
183
+ validation: {
184
+ maxValue: 900,
185
+ maxErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
186
+ minValue: 100,
187
+ minErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
188
+ },
189
+ backLabel: 'Question précédente',
190
+ label: 'Quel est votre coût moyen mensuel ?',
191
+ placeholder: 'Votre coût moyen mensuel',
192
+ actionLabel: 'Continuer',
193
+ image: null,
194
+ },
195
+ helpArea: [
196
+ {
197
+ type: 'text',
198
+ label: 'Trouvez facilement cette information',
199
+ },
200
+ {
201
+ type: 'link',
202
+ label: 'Comment trouver cette information',
203
+ action: {
204
+ type: 'MODAL',
205
+ dialogViewModel: {
206
+ headerTitle: 'Trouvez facilement cette information',
207
+ htmlContent:
208
+ "<ul><li>En ligne via un compte client en ligne ou une application mobile.</li><li>Sur votre facture d'électricité.</li></ul>",
209
+ },
210
+ },
211
+ },
212
+ ],
213
+ };
214
+
215
+ <Source language="json" code={JSON.stringify(withModal, null, ' ')} />
216
+
217
+ <Canvas>
218
+ <Story
219
+ name="Showcase - Feature with modal"
220
+ parameters={{ controls: { disable: true } }}
221
+ args={{ payload: withModal }}
222
+ >
223
+ {TemplateSandbox.bind({})}
224
+ </Story>
225
+ </Canvas>
226
+
227
+ ## With accordion for pedagogy
228
+
229
+ export const withAccordion = {
230
+ viewModel: {
231
+ validation: {
232
+ maxValue: 900,
233
+ maxErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
234
+ minValue: 100,
235
+ minErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
236
+ },
237
+ backLabel: 'Question précédente',
238
+ label: 'Quel est votre coût moyen mensuel ?',
239
+ placeholder: 'Votre coût moyen mensuel',
240
+ actionLabel: 'Continuer',
241
+ image: null,
242
+ },
243
+ pedagogy: {
244
+ component: 'MPbAccordion',
245
+ label: 'Trouvez facilement cette information',
246
+ content: [
247
+ {
248
+ htmlContent:
249
+ "<ul><li>En ligne via un compte client en ligne ou une application mobile.</li><li>Sur votre facture d'électricité.</li></ul>",
250
+ imgUrl: 'https://storage.googleapis.com/project-booster-media/energyrenovation/reference_tax_income.png',
251
+ },
252
+ ],
253
+ },
254
+ };
255
+
256
+ <Source language="json" code={JSON.stringify(withAccordion, null, ' ')} />
257
+
258
+ <Canvas>
259
+ <Story
260
+ name="Showcase - Feature with accordion for pedagogy"
261
+ parameters={{ controls: { disable: true } }}
262
+ args={{ payload: withAccordion }}
263
+ >
264
+ {TemplateSandbox.bind({})}
265
+ </Story>
266
+ </Canvas>
267
+
268
+ ## With help area and accordion
269
+
270
+ export const withHelpAreaAndAccordion = {
271
+ viewModel: {
272
+ validation: {
273
+ maxValue: 900,
274
+ maxErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
275
+ minValue: 100,
276
+ minErrorMessage: 'Le coût moyen mensuel de votre facture doit être comprise entre 100 et 900 euros',
277
+ },
278
+ backLabel: 'Question précédente',
279
+ label: 'Quel est votre coût moyen mensuel ?',
280
+ placeholder: 'Votre coût moyen mensuel',
281
+ actionLabel: 'Continuer',
282
+ image: null,
283
+ },
284
+ helpArea: [
285
+ {
286
+ type: 'link',
287
+ label: 'Comment trouver cette information',
288
+ action: {
289
+ type: 'MODAL',
290
+ dialogViewModel: {
291
+ headerTitle: 'Trouvez facilement cette information',
292
+ htmlContent:
293
+ "<ul><li>En ligne via un compte client en ligne ou une application mobile.</li><li>Sur votre facture d'électricité.</li></ul>",
294
+ },
295
+ },
296
+ },
297
+ ],
298
+ pedagogy: {
299
+ component: 'MPbAccordion',
300
+ label: 'Trouvez facilement cette information',
301
+ content: [
302
+ {
303
+ htmlContent:
304
+ "<ul><li>En ligne via un compte client en ligne ou une application mobile.</li><li>Sur votre facture d'électricité.</li></ul>",
305
+ imgUrl: 'https://storage.googleapis.com/project-booster-media/energyrenovation/reference_tax_income.png',
306
+ },
307
+ ],
308
+ },
309
+ };
310
+
311
+ <Source language="json" code={JSON.stringify(withHelpAreaAndAccordion, null, ' ')} />
312
+
313
+ <Canvas>
314
+ <Story
315
+ name="Showcase - Feature with help area and accordion"
316
+ parameters={{ controls: { disable: true } }}
317
+ args={{ payload: withHelpAreaAndAccordion }}
318
+ >
319
+ {TemplateSandbox.bind({})}
320
+ </Story>
321
+ </Canvas>