mod-build 3.7.13 → 3.7.15-beta.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/gulp-tasks/build.js +4 -4
  3. package/gulp-tasks/get-default-trade-questions.js +155 -0
  4. package/gulp-tasks/serve.js +5 -5
  5. package/gulp-tasks/tasks.js +11 -0
  6. package/gulp-tasks/templates.js +4 -4
  7. package/package.json +1 -1
  8. package/src/trade-questions/alerts_medical.js +111 -0
  9. package/src/trade-questions/bathroom_refacing.js +96 -0
  10. package/src/trade-questions/cabinet_refacing.js +251 -0
  11. package/src/trade-questions/cabinets.js +35 -0
  12. package/src/trade-questions/concrete_foundation.js +57 -0
  13. package/src/trade-questions/door.js +143 -0
  14. package/src/trade-questions/flooring.js +185 -0
  15. package/src/trade-questions/garage_door.js +89 -0
  16. package/src/trade-questions/gutters.js +45 -0
  17. package/src/trade-questions/home_security.js +130 -0
  18. package/src/trade-questions/home_warranty.js +57 -0
  19. package/src/trade-questions/hot_tubs.js +92 -0
  20. package/src/trade-questions/hvac.js +179 -0
  21. package/src/trade-questions/index.js +26 -0
  22. package/src/trade-questions/insulation.js +118 -0
  23. package/src/trade-questions/plumbing.js +260 -0
  24. package/src/trade-questions/roofing.js +118 -0
  25. package/src/trade-questions/siding.js +147 -0
  26. package/src/trade-questions/solar.js +69 -0
  27. package/src/trade-questions/stair_lifts.js +47 -0
  28. package/src/trade-questions/tree_services.js +161 -0
  29. package/src/trade-questions/walk_in_tubs.js +47 -0
  30. package/src/trade-questions/water_treatment.js +46 -0
  31. package/src/trade-questions/windows.js +132 -0
  32. package/src/trade-questions-config.js +139 -0
  33. package/tasks/grab-cdn.js +130 -0
@@ -0,0 +1,47 @@
1
+ /* eslint-disable */
2
+ const stair_lifts = {
3
+ ReasonForStairLift: {
4
+ fields: [
5
+ {
6
+ legend: {
7
+ text: 'Why are you considering a stair lift?'
8
+ },
9
+ options: [
10
+ {
11
+ attributes: {
12
+ name: 'ReasonForStairLift',
13
+ value: 'Safety',
14
+ checked: '',
15
+ data: {
16
+ required: 'nonempty'
17
+ }
18
+ },
19
+ text: 'Safety'
20
+ },
21
+ {
22
+ attributes: {
23
+ name: 'ReasonForStairLift',
24
+ value: 'Mobility',
25
+ data: {
26
+ required: 'nonempty'
27
+ }
28
+ },
29
+ text: 'Mobility'
30
+ },
31
+ {
32
+ attributes: {
33
+ name: 'ReasonForStairLift',
34
+ value: 'Other',
35
+ data: {
36
+ required: 'nonempty'
37
+ }
38
+ },
39
+ text: 'Other'
40
+ },
41
+ ]
42
+ }
43
+ ]
44
+ }
45
+ };
46
+
47
+ module.exports = stair_lifts;
@@ -0,0 +1,161 @@
1
+ /* eslint-disable */
2
+ const tree_services = {
3
+ JobType: {
4
+ fields: [
5
+ {
6
+ legend: {
7
+ text: 'Which tree service do you need?'
8
+ },
9
+ options: [
10
+ {
11
+ text: 'Tree Removal',
12
+ attributes: {
13
+ name: 'JobType',
14
+ value: 'Tree Removal',
15
+
16
+ }
17
+ },
18
+ {
19
+ text: 'Tree Trimming',
20
+ attributes: {
21
+ name: 'JobType',
22
+ value: 'Tree Trimming',
23
+
24
+ }
25
+ },
26
+ {
27
+ text: 'Stump Removal',
28
+ attributes: {
29
+ name: 'JobType',
30
+ value: 'Stump Removal',
31
+
32
+ }
33
+ },
34
+ {
35
+ text: 'Other',
36
+ attributes: {
37
+ name: 'JobType',
38
+ value: 'Other',
39
+
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ },
46
+ NumberOfTrees: {
47
+ fields: [
48
+ {
49
+ legend: {
50
+ text: 'How many trees/stumps are involved?'
51
+ },
52
+ options: [
53
+ {
54
+ text: '1',
55
+ attributes: {
56
+ name: 'NumberOfTrees',
57
+ value: '1',
58
+ data: {
59
+ required: 'nonempty'
60
+ }
61
+ }
62
+ },
63
+ {
64
+ text: '2',
65
+ attributes: {
66
+ name: 'NumberOfTrees',
67
+ value: '2',
68
+ data: {
69
+ required: 'nonempty'
70
+ }
71
+ }
72
+ },
73
+ {
74
+ text: '3',
75
+ attributes: {
76
+ name: 'NumberOfTrees',
77
+ value: '3',
78
+ data: {
79
+ required: 'nonempty'
80
+ }
81
+ }
82
+ },
83
+ {
84
+ text: '4+',
85
+
86
+ attributes: {
87
+ name: 'NumberOfTrees',
88
+ value: '4+',
89
+ data: {
90
+ required: 'nonempty'
91
+ }
92
+ }
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ },
98
+ TreeSize: {
99
+ fields: [
100
+ {
101
+ legend: {
102
+ text: 'What are the tree sizes?'
103
+ },
104
+ options: [
105
+ {
106
+ text: '<1 Story',
107
+ attributes: {
108
+ name: 'TreeSize',
109
+ value: '<1 story',
110
+ data: {
111
+ required: 'nonempty'
112
+ }
113
+ }
114
+ },
115
+ {
116
+ text: '1 Story',
117
+ attributes: {
118
+ name: 'TreeSize',
119
+ value: '1 story',
120
+ data: {
121
+ required: 'nonempty'
122
+ }
123
+ }
124
+ },
125
+ {
126
+ text: '2 Stories',
127
+ attributes: {
128
+ name: 'TreeSize',
129
+ value: '2 stories',
130
+ data: {
131
+ required: 'nonempty'
132
+ }
133
+ }
134
+ },
135
+ {
136
+ text: '3 Stories',
137
+ attributes: {
138
+ name: 'TreeSize',
139
+ value: '3 stories',
140
+ data: {
141
+ required: 'nonempty'
142
+ }
143
+ }
144
+ },
145
+ {
146
+ text: 'Mix of Sizes',
147
+ attributes: {
148
+ name: 'TreeSize',
149
+ value: 'mix of sizes',
150
+ data: {
151
+ required: 'nonempty'
152
+ }
153
+ }
154
+ }
155
+ ]
156
+ }
157
+ ]
158
+ }
159
+ };
160
+
161
+ module.exports = tree_services;
@@ -0,0 +1,47 @@
1
+ /* eslint-disable */
2
+ const walk_in_tubs = {
3
+ Interest: {
4
+ fields: [
5
+ {
6
+ legend: {
7
+ text: 'What\'s the biggest reason you are considering a walk-in&nbsp;tub?'
8
+ },
9
+ options: [
10
+ {
11
+ text: 'For Safety',
12
+ attributes: {
13
+ name: 'Interest',
14
+ value: 'Safety',
15
+ checked: '',
16
+ data: {
17
+ required: 'nonempty'
18
+ }
19
+ }
20
+ },
21
+ {
22
+ text: 'For Therapeutic Reasons',
23
+ attributes: {
24
+ name: 'Interest',
25
+ value: 'Therapeutic',
26
+ data: {
27
+ required: 'nonempty'
28
+ }
29
+ }
30
+ },
31
+ {
32
+ text: 'Other Reasons',
33
+ attributes: {
34
+ name: 'Interest',
35
+ value: 'Other',
36
+ data: {
37
+ required: 'nonempty'
38
+ }
39
+ }
40
+ }
41
+ ]
42
+ }
43
+ ]
44
+ }
45
+ };
46
+
47
+ module.exports = walk_in_tubs;
@@ -0,0 +1,46 @@
1
+
2
+ /* eslint-disable */
3
+ const water_treatment = {
4
+ fields: [
5
+ {
6
+ legend: {
7
+ text: 'What type of water do you&nbsp;have?'
8
+ },
9
+ options: [
10
+ {
11
+ attributes: {
12
+ name: 'ServiceType',
13
+ value: 'CITY_WATER',
14
+ checked: '',
15
+ data: {
16
+ required: 'nonempty'
17
+ }
18
+ },
19
+ text: 'City Water'
20
+ },
21
+ {
22
+ attributes: {
23
+ name: 'ServiceType',
24
+ value: 'WELL_WATER',
25
+ data: {
26
+ required: 'nonempty'
27
+ }
28
+ },
29
+ text: 'Well Water'
30
+ },
31
+ {
32
+ attributes: {
33
+ name: 'ServiceType',
34
+ value: 'UNSURE',
35
+ data: {
36
+ required: 'nonempty'
37
+ }
38
+ },
39
+ text: 'Unsure'
40
+ }
41
+ ]
42
+ }
43
+ ]
44
+ };
45
+
46
+ module.exports = water_treatment;
@@ -0,0 +1,132 @@
1
+ const windows = {
2
+ WindowsProjectScope: {
3
+ fields: [
4
+ {
5
+ legend: {
6
+ text: 'What is the nature of your windows&nbsp;project?'
7
+ },
8
+ options: [
9
+ {
10
+ attributes: {
11
+ name: 'WindowsProjectScope',
12
+ value: 'Install',
13
+ data: {
14
+ required: 'nonempty'
15
+ }
16
+ },
17
+ text: 'Replace'
18
+ },
19
+ {
20
+ attributes: {
21
+ name: 'WindowsProjectScope',
22
+ value: 'Repair',
23
+ data: {
24
+ required: 'nonempty'
25
+ }
26
+ },
27
+ text: 'Repair'
28
+ },
29
+ {
30
+ attributes: {
31
+ name: 'WindowsProjectScope',
32
+ value: 'Install',
33
+ data: {
34
+ required: 'nonempty'
35
+ }
36
+ },
37
+ text: 'Not Sure'
38
+ }
39
+ ]
40
+ }
41
+ ]
42
+ },
43
+ NumberOfWindows: {
44
+ fields: [
45
+ {
46
+ fieldType: 'radio',
47
+ radioOptionType: 'custom',
48
+ legend: {
49
+ text: 'How many windows are&nbsp;involved?'
50
+ },
51
+ options: [
52
+ {
53
+ attributes: {
54
+ name: 'NumberOfWindows',
55
+ value: '6-9',
56
+ data: {
57
+ required: 'nonempty'
58
+ }
59
+ },
60
+ text: '6+'
61
+ },
62
+ {
63
+ attributes: {
64
+ name: 'NumberOfWindows',
65
+ value: '3-5',
66
+ data: {
67
+ required: 'nonempty'
68
+ }
69
+ },
70
+ text: '3-5'
71
+ },
72
+ {
73
+ attributes: {
74
+ name: 'NumberOfWindows',
75
+ value: '2',
76
+ data: {
77
+ required: 'nonempty'
78
+ }
79
+ },
80
+ text: '2'
81
+ },
82
+ {
83
+ attributes: {
84
+ name: 'NumberOfWindows',
85
+ value: '1',
86
+ data: {
87
+ required: 'nonempty'
88
+ }
89
+ },
90
+ text: '1'
91
+ }
92
+ ]
93
+ }
94
+ ],
95
+ extraQuestionBlock: {
96
+ fields: [
97
+ {
98
+ attributes: {
99
+ id: 'MultipleWindows'
100
+ },
101
+ legend: {
102
+ text: 'Would you be open to a quote for Multiple&nbsp;windows? <span class="step__subtitle">(Most contractors in your area only service projects for multiple windows)</span>'
103
+ },
104
+ options: [
105
+ {
106
+ attributes: {
107
+ name: 'MultipleWindows',
108
+ value: 'Yes',
109
+ data: {
110
+ required: 'nonempty'
111
+ }
112
+ },
113
+ text: 'Yes'
114
+ },
115
+ {
116
+ attributes: {
117
+ name: 'MultipleWindows',
118
+ value: 'No',
119
+ data: {
120
+ required: 'nonempty'
121
+ }
122
+ },
123
+ text: 'No'
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ }
129
+ }
130
+ };
131
+
132
+ module.exports = windows;
@@ -0,0 +1,139 @@
1
+ /* eslint-disable */
2
+ const {
3
+ alerts_medical,
4
+ bathroom_refacing,
5
+ cabinets,
6
+ cabinet_refacing,
7
+ concrete_foundation,
8
+ door,
9
+ flooring,
10
+ garage_door,
11
+ gutters,
12
+ home_security,
13
+ home_warranty,
14
+ hot_tubs,
15
+ hvac,
16
+ insulation,
17
+ plumbing,
18
+ roofing,
19
+ siding,
20
+ solar,
21
+ stair_lifts,
22
+ tree_services,
23
+ walk_in_tubs,
24
+ water_treatment,
25
+ windows
26
+ } = require('./trade-questions');
27
+
28
+ const tradeQuestionsConfig = {
29
+ tradeQuestions: {
30
+ alerts_medical,
31
+ bathroom_refacing,
32
+ cabinets,
33
+ cabinet_refacing,
34
+ concrete_foundation,
35
+ door,
36
+ flooring,
37
+ garage_door,
38
+ gutters,
39
+ home_security,
40
+ home_warranty,
41
+ hot_tubs,
42
+ hvac,
43
+ insulation,
44
+ plumbing,
45
+ roofing,
46
+ siding,
47
+ solar,
48
+ stair_lifts,
49
+ tree_services,
50
+ walk_in_tubs,
51
+ water_treatment,
52
+ windows
53
+ },
54
+ OwnHome: {
55
+ fields: [
56
+ {
57
+ legend: {
58
+ text: 'Do you own your&nbsp;home?'
59
+ },
60
+ options: [
61
+ {
62
+ attributes: {
63
+ name: 'OwnHome',
64
+ value: 'Yes',
65
+ data: {
66
+ required: 'nonempty'
67
+ }
68
+ },
69
+ text: 'Yes'
70
+ },
71
+ {
72
+ attributes: {
73
+ class: 'multiple-lines',
74
+ name: 'OwnHome',
75
+ value: 'Authorized',
76
+ data: {
77
+ required: 'nonempty'
78
+ }
79
+ },
80
+ text: 'No, but I\'m allowed to make changes'
81
+ },
82
+ {
83
+ attributes: {
84
+ name: 'OwnHome',
85
+ value: 'No',
86
+ data: {
87
+ required: 'nonempty'
88
+ }
89
+ },
90
+ text: 'No'
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ },
96
+ BuyTimeframe: {
97
+ fields: [
98
+ {
99
+ legend: {
100
+ text: 'When do you need to start this&nbsp;project?'
101
+ },
102
+ options: [
103
+ {
104
+ text: 'Immediately',
105
+ attributes: {
106
+ name: 'BuyTimeframe',
107
+ value: 'Immediately',
108
+ data: {
109
+ required: 'nonempty'
110
+ }
111
+ }
112
+ },
113
+ {
114
+ text: 'Within 6 months',
115
+ attributes: {
116
+ name: 'BuyTimeframe',
117
+ value: '4-6 months',
118
+ data: {
119
+ required: 'nonempty'
120
+ }
121
+ }
122
+ },
123
+ {
124
+ text: 'Not Sure',
125
+ attributes: {
126
+ name: 'BuyTimeframe',
127
+ value: 'Don\'t know',
128
+ data: {
129
+ required: 'nonempty'
130
+ }
131
+ }
132
+ }
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ };
138
+
139
+ module.exports = tradeQuestionsConfig;
@@ -0,0 +1,130 @@
1
+ import { defaultSettings } from '../src/data/config.js';
2
+ import axios from 'axios';
3
+ import { createWriteStream } from 'node:fs';
4
+ import * as stream from 'node:stream';
5
+ import { promisify } from 'node:util';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { responseInterceptor } from '../src/scripts/retry-axios.js';
9
+
10
+ const axiosInstance = axios.create();
11
+ responseInterceptor(axiosInstance);
12
+
13
+ async function duplicateHtmlFilestoHbs(externalResources) {
14
+ for (const inputPath in externalResources) {
15
+ const [destPath, fileName] = externalResources[inputPath];
16
+ const fileExtension = path.extname(fileName);
17
+
18
+ if (fileExtension === '.html') {
19
+ const newFileName = fileName.replace('.html', '.hbs');
20
+ const sourceFilePath = path.join(destPath, fileName);
21
+ const destinationFilePath = path.join(destPath, newFileName);
22
+
23
+ if (fs.existsSync(sourceFilePath) && !fs.existsSync(destinationFilePath)) {
24
+ fs.copyFileSync(sourceFilePath, destinationFilePath);
25
+ console.log(`${sourceFilePath} duplicated as ${destinationFilePath}`);
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ function streamToDestination(inputPath, destPath, fileName) {
32
+ const finished = promisify(stream.finished);
33
+ const url = `https://${defaultSettings.nodeEnv}${inputPath}`;
34
+
35
+ return new Promise(resolve => {
36
+ if (!fs.existsSync(`${destPath}`)) {
37
+ fs.mkdirSync(`${destPath}`, { recursive: true });
38
+ }
39
+
40
+ // if file exists, do not create it again
41
+ if (fs.existsSync(`${destPath}${fileName}`)) {
42
+ resolve();
43
+ } else {
44
+ const writer = createWriteStream(`${destPath}${fileName}`);
45
+ const options = {
46
+ url,
47
+ method: 'get',
48
+ responseType: 'stream',
49
+ };
50
+
51
+ axiosInstance(options).then(resp => {
52
+ if (resp.status !== 200) {
53
+ throw new Error(`${resp.status}: Error while fetching ${url}`);
54
+ }
55
+ resp.data.pipe(writer);
56
+ console.log(`${destPath}${fileName} created...`);
57
+ return finished(writer);
58
+ }).then(() => {
59
+ resolve();
60
+ }).catch(error => {
61
+ console.error(error);
62
+ process.exit(1);
63
+ }).finally(() => {
64
+ axiosInstance.interceptors.response.eject(responseInterceptor);
65
+ });
66
+ }
67
+ });
68
+ }
69
+
70
+ export default async function(config) {
71
+ const { nodeEnv, srcFolder, publicFolder } = defaultSettings;
72
+ const { isQSPage, isWhiteLabel, page } = config;
73
+ const themeFile = page?.themeFile;
74
+ const cssThemes = page?.cssThemes;
75
+ const isModWhiteLabel = isWhiteLabel && !isQSPage
76
+ const domainHasModernize = config?.domain?.indexOf('modernize') > -1 && !(config?.domain?.includes('bestcompany'));
77
+
78
+ let externalResources;
79
+ // listing of Static Resources sub-path and site destination paths
80
+ // key: inputPath, value: destPath
81
+
82
+ externalResources = {
83
+ '/quote/resources/mod-site/templates/scripts/trusted-form.html': [`${publicFolder}/resources/scripts/`, 'trusted-form.html'],
84
+ '/quote/resources/shared-resources/scripts/geolocation/geolocation.min.js': [`${publicFolder}/resources/scripts/geolocation/`, 'geolocation.min.js'],
85
+ '/quote/resources/data/tcpa.json': [`${srcFolder}/resources/data/`, 'tcpa.json']
86
+ };
87
+
88
+ // grab footer modals and place under the resources
89
+ Object.assign(externalResources, {
90
+ '/quote/resources/shared-resources/templates/modals/about/': [`${srcFolder}/resources/templates/modals/about/`, 'index.html'],
91
+ '/quote/resources/shared-resources/templates/modals/privacy/': [`${srcFolder}/resources/templates/modals/privacy/`, 'index.html'],
92
+ '/quote/resources/shared-resources/templates/modals/terms/': [`${srcFolder}/resources/templates/modals/terms/`, 'index.html'],
93
+ '/quote/resources/shared-resources/templates/modals/contact-us/': [`${srcFolder}/resources/templates/modals/contact-us/`, 'index.html'],
94
+ '/quote/resources/shared-resources/templates/modals/faq/': [`${srcFolder}/resources/templates/modals/faq/`, 'index.html'],
95
+ '/quote/resources/shared-resources/templates/modals/e-sign-consent/': [`${srcFolder}/resources/templates/modals/e-sign-consent/`, 'index.html'],
96
+ '/quote/resources/shared-resources/templates/modals/clients/': [`${srcFolder}/resources/templates/modals/clients/`, 'index.html']
97
+ })
98
+
99
+ if (isModWhiteLabel || domainHasModernize) {
100
+ Object.assign(externalResources, {'/quote/resources/mod-site/templates/scripts/recaptcha.html': [`${publicFolder}/resources/scripts/`, 'recaptcha.html']});
101
+ }
102
+
103
+ if (cssThemes && cssThemes.length > 0) {
104
+ cssThemes.forEach(theme => {
105
+ const themeFileName = `_${theme}.scss`;
106
+ const themePath = `/quote/resources/shared-resources/styles/themes/${themeFileName}`;
107
+ Object.assign(externalResources, {[themePath]: [`${srcFolder}/resources/styles/themes/`, themeFileName]});
108
+ });
109
+ }
110
+
111
+ // theme JSON
112
+ if (isQSPage && themeFile) {
113
+ Object.assign(externalResources, {
114
+ [`/quote/resources/data/themes/${themeFile}.json`]: [`${srcFolder}/data/`, 'theme.json']
115
+ });
116
+ }
117
+
118
+ if (!nodeEnv) {
119
+ throw new Error('Missing environment variables. Did you start with gulp instead of npm run...?');
120
+ }
121
+
122
+ const filesPromiseMap = Object.keys(externalResources).map(async key => {
123
+ const destinationPath = externalResources[key][0];
124
+ const fileName = externalResources[key][1];
125
+ await streamToDestination(key, destinationPath, fileName);
126
+ });
127
+
128
+ await Promise.all(filesPromiseMap);
129
+ await duplicateHtmlFilestoHbs(externalResources);
130
+ }